agora inbox for [email protected]
help / color / mirror / Atom feed[PATCH v31 07/11] Add DISTINCT support for IVM
953+ messages / 4 participants
[nested] [flat]
* [PATCH v31 07/11] Add DISTINCT support for IVM
@ 2023-05-31 10:08 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Yugo Nagata @ 2023-05-31 10:08 UTC (permalink / raw)
When IMMV is created with DISTINCT, multiplicity of tuples is
counted and stored in "__ivm_count__" column, which is a hidden
column of IMMV. The value in __ivm_count__ is updated when IMMV
is maintained incrementally. A tuple in IMMV can be removed if
and only if the count becomes zero.
---
src/backend/commands/createas.c | 141 ++++++++++++++++++++------
src/backend/commands/indexcmds.c | 40 ++++++++
src/backend/commands/matview.c | 148 ++++++++++++++++++++++++++--
src/backend/commands/tablecmds.c | 9 ++
src/backend/nodes/outfuncs.c | 1 +
src/backend/nodes/readfuncs.c | 1 +
src/backend/parser/parse_relation.c | 18 +++-
src/backend/rewrite/rewriteDefine.c | 3 +-
src/include/commands/createas.h | 2 +
src/include/nodes/parsenodes.h | 2 +
10 files changed, 320 insertions(+), 45 deletions(-)
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 04a5ee9e37..8f2bd5203e 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -50,6 +50,7 @@
#include "parser/parser.h"
#include "parser/parsetree.h"
#include "parser/parse_clause.h"
+#include "parser/parse_func.h"
#include "rewrite/rewriteHandler.h"
#include "tcop/tcopprot.h"
#include "utils/builtins.h"
@@ -305,6 +306,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
errhint("functions must be marked IMMUTABLE")));
check_ivm_restriction((Node *) query);
+
+ /* For IMMV, we need to rewrite matview query */
+ query = rewriteQueryForIMMV(query, into->colNames);
}
if (into->skipData)
@@ -409,6 +413,49 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
return address;
}
+/*
+ * rewriteQueryForIMMV -- rewrite view definition query for IMMV
+ *
+ * count(*) is added for counting distinct tuples in views.
+ */
+Query *
+rewriteQueryForIMMV(Query *query, List *colNames)
+{
+ Query *rewritten;
+
+ Node *node;
+ ParseState *pstate = make_parsestate(NULL);
+ FuncCall *fn;
+
+ rewritten = copyObject(query);
+ pstate->p_expr_kind = EXPR_KIND_SELECT_TARGET;
+
+ /*
+ * Convert DISTINCT to GROUP BY and add count(*) for counting distinct
+ * tuples in views.
+ */
+ if (rewritten->distinctClause)
+ {
+ TargetEntry *tle;
+
+ rewritten->groupClause = transformDistinctClause(NULL, &rewritten->targetList, rewritten->sortClause, false);
+
+ fn = makeFuncCall(SystemFuncName("count"), NIL, COERCE_EXPLICIT_CALL, -1);
+ fn->agg_star = true;
+
+ node = ParseFuncOrColumn(pstate, fn->funcname, NIL, NULL, fn, false, -1);
+
+ tle = makeTargetEntry((Expr *) node,
+ list_length(rewritten->targetList) + 1,
+ pstrdup("__ivm_count__"),
+ false);
+ rewritten->targetList = lappend(rewritten->targetList, tle);
+ rewritten->hasAggs = true;
+ }
+
+ return rewritten;
+}
+
/*
* GetIntoRelEFlags --- compute executor flags needed for CREATE TABLE AS
*
@@ -532,7 +579,8 @@ intorel_startup(DestReceiver *self, int operation, TupleDesc typeinfo)
ColumnDef *col;
char *colname;
- if (lc)
+ /* Don't override hidden columns added for IVM */
+ if (lc && !isIvmName(NameStr(attribute->attname)))
{
colname = strVal(lfirst(lc));
lc = lnext(into->colNames, lc);
@@ -936,10 +984,6 @@ check_ivm_restriction_walker(Node *node, void *context)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("LIMIT/OFFSET clause is not supported on incrementally maintainable materialized view")));
- if (qry->distinctClause)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("DISTINCT is not supported on incrementally maintainable materialized view")));
if (qry->hasDistinctOn)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
@@ -1086,12 +1130,18 @@ CreateIndexOnIMMV(Query *query, Relation matviewRel)
char idxname[NAMEDATALEN];
List *indexoidlist = RelationGetIndexList(matviewRel);
ListCell *indexoidscan;
- Bitmapset *key_attnos;
snprintf(idxname, sizeof(idxname), "%s_index", RelationGetRelationName(matviewRel));
index = makeNode(IndexStmt);
+ /*
+ * We consider null values not distinct to make sure that views with DISTINCT
+ * or GROUP BY don't contain multiple NULL rows when NULL is inserted to
+ * a base table concurrently.
+ */
+ index->nulls_not_distinct = true;
+
index->unique = true;
index->primary = false;
index->isconstraint = false;
@@ -1118,41 +1168,68 @@ CreateIndexOnIMMV(Query *query, Relation matviewRel)
index->concurrent = false;
index->if_not_exists = false;
- /* create index on the base tables' primary key columns */
- key_attnos = get_primary_key_attnos_from_query(query, &constraintList);
- if (key_attnos)
+ if (query->distinctClause)
{
+ /* create unique constraint on all columns */
foreach(lc, query->targetList)
{
TargetEntry *tle = (TargetEntry *) lfirst(lc);
Form_pg_attribute attr = TupleDescAttr(matviewRel->rd_att, tle->resno - 1);
-
- if (bms_is_member(tle->resno - FirstLowInvalidHeapAttributeNumber, key_attnos))
- {
- IndexElem *iparam;
-
- iparam = makeNode(IndexElem);
- iparam->name = pstrdup(NameStr(attr->attname));
- iparam->expr = NULL;
- iparam->indexcolname = NULL;
- iparam->collation = NIL;
- iparam->opclass = NIL;
- iparam->opclassopts = NIL;
- iparam->ordering = SORTBY_DEFAULT;
- iparam->nulls_ordering = SORTBY_NULLS_DEFAULT;
- index->indexParams = lappend(index->indexParams, iparam);
- }
+ IndexElem *iparam;
+
+ iparam = makeNode(IndexElem);
+ iparam->name = pstrdup(NameStr(attr->attname));
+ iparam->expr = NULL;
+ iparam->indexcolname = NULL;
+ iparam->collation = NIL;
+ iparam->opclass = NIL;
+ iparam->opclassopts = NIL;
+ iparam->ordering = SORTBY_DEFAULT;
+ iparam->nulls_ordering = SORTBY_NULLS_DEFAULT;
+ index->indexParams = lappend(index->indexParams, iparam);
}
}
else
{
- /* create no index, just notice that an appropriate index is necessary for efficient IVM */
- ereport(NOTICE,
- (errmsg("could not create an index on materialized view \"%s\" automatically",
- RelationGetRelationName(matviewRel)),
- errdetail("This target list does not have all the primary key columns. "),
- errhint("Create an index on the materialized view for efficient incremental maintenance.")));
- return;
+ Bitmapset *key_attnos;
+
+ /* create index on the base tables' primary key columns */
+ key_attnos = get_primary_key_attnos_from_query(query, &constraintList);
+ if (key_attnos)
+ {
+ foreach(lc, query->targetList)
+ {
+ TargetEntry *tle = (TargetEntry *) lfirst(lc);
+ Form_pg_attribute attr = TupleDescAttr(matviewRel->rd_att, tle->resno - 1);
+
+ if (bms_is_member(tle->resno - FirstLowInvalidHeapAttributeNumber, key_attnos))
+ {
+ IndexElem *iparam;
+
+ iparam = makeNode(IndexElem);
+ iparam->name = pstrdup(NameStr(attr->attname));
+ iparam->expr = NULL;
+ iparam->indexcolname = NULL;
+ iparam->collation = NIL;
+ iparam->opclass = NIL;
+ iparam->opclassopts = NIL;
+ iparam->ordering = SORTBY_DEFAULT;
+ iparam->nulls_ordering = SORTBY_NULLS_DEFAULT;
+ index->indexParams = lappend(index->indexParams, iparam);
+ }
+ }
+ }
+ else
+ {
+ /* create no index, just notice that an appropriate index is necessary for efficient IVM */
+ ereport(NOTICE,
+ (errmsg("could not create an index on materialized view \"%s\" automatically",
+ RelationGetRelationName(matviewRel)),
+ errdetail("This target list does not have all the primary key columns, "
+ "or this view does not contain DISTINCT clause."),
+ errhint("Create an index on the materialized view for efficient incremental maintenance.")));
+ return;
+ }
}
/* If we have a compatible index, we don't need to create another. */
diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c
index d9016ef487..fb5265e6c3 100644
--- a/src/backend/commands/indexcmds.c
+++ b/src/backend/commands/indexcmds.c
@@ -41,6 +41,7 @@
#include "commands/dbcommands.h"
#include "commands/defrem.h"
#include "commands/event_trigger.h"
+#include "commands/matview.h"
#include "commands/progress.h"
#include "commands/tablecmds.h"
#include "commands/tablespace.h"
@@ -1132,6 +1133,45 @@ DefineIndex(Oid tableId,
safe_index = indexInfo->ii_Expressions == NIL &&
indexInfo->ii_Predicate == NIL;
+ /*
+ * We disallow unique indexes on IVM columns of IMMVs.
+ */
+ if (RelationIsIVM(rel) && stmt->unique)
+ {
+ for (int i = 0; i < indexInfo->ii_NumIndexKeyAttrs; i++)
+ {
+ AttrNumber attno = indexInfo->ii_IndexAttrNumbers[i];
+ if (attno > 0)
+ {
+ char *name = NameStr(TupleDescAttr(rel->rd_att, attno - 1)->attname);
+ if (name && isIvmName(name))
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("unique index creation on IVM columns is not supported")));
+ }
+ }
+
+ if (indexInfo->ii_Expressions)
+ {
+ Bitmapset *indexattrs = NULL;
+ int varno = -1;
+
+ pull_varattnos((Node *) indexInfo->ii_Expressions, 1, &indexattrs);
+
+ while ((varno = bms_next_member(indexattrs, varno)) >= 0)
+ {
+ int attno = varno + FirstLowInvalidHeapAttributeNumber;
+ char *name = NameStr(TupleDescAttr(rel->rd_att, attno - 1)->attname);
+ if (name && isIvmName(name))
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("unique index creation on IVM columns is not supported")));
+ }
+
+ }
+ }
+
+
/*
* Report index creation if appropriate (delay this till after most of the
* error checks)
diff --git a/src/backend/commands/matview.c b/src/backend/commands/matview.c
index 1061c37b2c..f2e8aa02a3 100644
--- a/src/backend/commands/matview.c
+++ b/src/backend/commands/matview.c
@@ -148,11 +148,15 @@ static Query *rewrite_query_for_postupdate_state(Query *query, MV_TriggerTable *
static void apply_delta(Oid matviewOid, Tuplestorestate *old_tuplestores, Tuplestorestate *new_tuplestores,
TupleDesc tupdesc_old, TupleDesc tupdesc_new,
- Query *query);
+ Query *query, bool use_count, char *count_colname);
static void apply_old_delta(const char *matviewname, const char *deltaname_old,
List *keys);
+static void apply_old_delta_with_count(const char *matviewname, const char *deltaname_old,
+ List *keys, const char *count_colname);
static void apply_new_delta(const char *matviewname, const char *deltaname_new,
StringInfo target_list);
+static void apply_new_delta_with_count(const char *matviewname, const char* deltaname_new,
+ List *keys, StringInfo target_list, const char* count_colname);
static char *get_matching_condition_string(List *keys);
static void generate_equal(StringInfo querybuf, Oid opttype,
const char *leftop, const char *rightop);
@@ -267,6 +271,7 @@ ExecRefreshMatView(RefreshMatViewStmt *stmt, const char *queryString,
Oid matviewOid;
Relation matviewRel;
Query *dataQuery;
+ Query *viewQuery;
Oid tableSpace;
Oid relowner;
Oid OIDNewHeap;
@@ -329,8 +334,13 @@ ExecRefreshMatView(RefreshMatViewStmt *stmt, const char *queryString,
"CONCURRENTLY", "WITH NO DATA")));
- dataQuery = get_matview_query(matviewRel);
+ viewQuery = get_matview_query(matviewRel);
+ /* For IMMV, we need to rewrite matview query */
+ if (!stmt->skipData && RelationIsIVM(matviewRel))
+ dataQuery = rewriteQueryForIMMV(viewQuery,NIL);
+ else
+ dataQuery = viewQuery;
/*
* Check that there is a unique index with no WHERE clause on one or more
@@ -510,8 +520,8 @@ ExecRefreshMatView(RefreshMatViewStmt *stmt, const char *queryString,
if (!stmt->skipData && RelationIsIVM(matviewRel) && !oldPopulated)
{
- CreateIndexOnIMMV(dataQuery, matviewRel);
- CreateIvmTriggersOnBaseTables(dataQuery, matviewOid);
+ CreateIndexOnIMMV(viewQuery, matviewRel);
+ CreateIvmTriggersOnBaseTables(viewQuery, matviewOid);
}
table_close(matviewRel, NoLock);
@@ -1533,6 +1543,13 @@ IVM_immediate_maintenance(PG_FUNCTION_ARGS)
int rte_index = lfirst_int(lc2);
TupleDesc tupdesc_old;
TupleDesc tupdesc_new;
+ bool use_count = false;
+ char *count_colname = NULL;
+
+ count_colname = pstrdup("__ivm_count__");
+
+ if (query->distinctClause)
+ use_count = true;
/* calculate delta tables */
calc_delta(table, rte_index, rewritten, dest_old, dest_new,
@@ -1545,7 +1562,8 @@ IVM_immediate_maintenance(PG_FUNCTION_ARGS)
{
/* apply the delta tables to the materialized view */
apply_delta(matviewOid, old_tuplestore, new_tuplestore,
- tupdesc_old, tupdesc_new, query);
+ tupdesc_old, tupdesc_new, query, use_count,
+ count_colname);
}
PG_CATCH();
{
@@ -2018,7 +2036,7 @@ rewrite_query_for_postupdate_state(Query *query, MV_TriggerTable *table, int rte
static void
apply_delta(Oid matviewOid, Tuplestorestate *old_tuplestores, Tuplestorestate *new_tuplestores,
TupleDesc tupdesc_old, TupleDesc tupdesc_new,
- Query *query)
+ Query *query, bool use_count, char *count_colname)
{
StringInfoData querybuf;
StringInfoData target_list_buf;
@@ -2094,7 +2112,12 @@ apply_delta(Oid matviewOid, Tuplestorestate *old_tuplestores, Tuplestorestate *n
if (rc != SPI_OK_REL_REGISTER)
elog(ERROR, "SPI_register failed");
- apply_old_delta(matviewname, OLD_DELTA_ENRNAME, keys);
+ if (use_count)
+ /* apply old delta and get rows to be recalculated */
+ apply_old_delta_with_count(matviewname, OLD_DELTA_ENRNAME,
+ keys, count_colname);
+ else
+ apply_old_delta(matviewname, OLD_DELTA_ENRNAME, keys);
}
/* For tuple insertion */
@@ -2116,7 +2139,11 @@ apply_delta(Oid matviewOid, Tuplestorestate *old_tuplestores, Tuplestorestate *n
elog(ERROR, "SPI_register failed");
/* apply new delta */
- apply_new_delta(matviewname, NEW_DELTA_ENRNAME, &target_list_buf);
+ if (use_count)
+ apply_new_delta_with_count(matviewname, NEW_DELTA_ENRNAME,
+ keys, &target_list_buf, count_colname);
+ else
+ apply_new_delta(matviewname, NEW_DELTA_ENRNAME, &target_list_buf);
}
/* We're done maintaining the materialized view. */
@@ -2129,6 +2156,51 @@ apply_delta(Oid matviewOid, Tuplestorestate *old_tuplestores, Tuplestorestate *n
elog(ERROR, "SPI_finish failed");
}
+/*
+ * apply_old_delta_with_count
+ *
+ * Execute a query for applying a delta table given by deltname_old
+ * which contains tuples to be deleted from to a materialized view given by
+ * matviewname. This is used when counting is required, that is, the view
+ * has aggregate or distinct.
+ */
+static void
+apply_old_delta_with_count(const char *matviewname, const char *deltaname_old,
+ List *keys, const char *count_colname)
+{
+ StringInfoData querybuf;
+ char *match_cond;
+
+ /* build WHERE condition for searching tuples to be deleted */
+ match_cond = get_matching_condition_string(keys);
+
+ /* Search for matching tuples from the view and update or delete if found. */
+ initStringInfo(&querybuf);
+ appendStringInfo(&querybuf,
+ "WITH t AS (" /* collecting tid of target tuples in the view */
+ "SELECT diff.%s, " /* count column */
+ "(diff.%s OPERATOR(pg_catalog.=) mv.%s) AS for_dlt, "
+ "mv.ctid "
+ "FROM %s AS mv, %s AS diff "
+ "WHERE %s" /* tuple matching condition */
+ "), updt AS (" /* update a tuple if this is not to be deleted */
+ "UPDATE %s AS mv SET %s = mv.%s OPERATOR(pg_catalog.-) t.%s "
+ "FROM t WHERE mv.ctid OPERATOR(pg_catalog.=) t.ctid AND NOT for_dlt "
+ ")"
+ /* delete a tuple if this is to be deleted */
+ "DELETE FROM %s AS mv USING t "
+ "WHERE mv.ctid OPERATOR(pg_catalog.=) t.ctid AND for_dlt",
+ count_colname,
+ count_colname, count_colname,
+ matviewname, deltaname_old,
+ match_cond,
+ matviewname, count_colname, count_colname, count_colname,
+ matviewname);
+
+ if (SPI_exec(querybuf.data, 0) != SPI_OK_DELETE)
+ elog(ERROR, "SPI_exec failed: %s", querybuf.data);
+}
+
/*
* apply_old_delta
*
@@ -2178,6 +2250,66 @@ apply_old_delta(const char *matviewname, const char *deltaname_old,
elog(ERROR, "SPI_exec failed: %s", querybuf.data);
}
+/*
+ * apply_new_delta_with_count
+ *
+ * Execute a query for applying a delta table given by deltname_new
+ * which contains tuples to be inserted into a materialized view given by
+ * matviewname. This is used when counting is required, that is, the view
+ * has aggregate or distinct. Also, when a table in EXISTS sub queries
+ * is modified.
+ */
+static void
+apply_new_delta_with_count(const char *matviewname, const char* deltaname_new,
+ List *keys, StringInfo target_list, const char* count_colname)
+{
+ StringInfoData querybuf;
+ StringInfoData returning_keys;
+ ListCell *lc;
+ char *match_cond = "";
+
+ /* build WHERE condition for searching tuples to be updated */
+ match_cond = get_matching_condition_string(keys);
+
+ /* build string of keys list */
+ initStringInfo(&returning_keys);
+ if (keys)
+ {
+ foreach (lc, keys)
+ {
+ Form_pg_attribute attr = (Form_pg_attribute) lfirst(lc);
+ char *resname = NameStr(attr->attname);
+ appendStringInfo(&returning_keys, "%s", quote_qualified_identifier("mv", resname));
+ if (lnext(keys, lc))
+ appendStringInfo(&returning_keys, ", ");
+ }
+ }
+ else
+ appendStringInfo(&returning_keys, "NULL");
+
+ /* Search for matching tuples from the view and update if found or insert if not. */
+ initStringInfo(&querybuf);
+ appendStringInfo(&querybuf,
+ "WITH updt AS (" /* update a tuple if this exists in the view */
+ "UPDATE %s AS mv SET %s = mv.%s OPERATOR(pg_catalog.+) diff.%s "
+ "FROM %s AS diff "
+ "WHERE %s " /* tuple matching condition */
+ "RETURNING %s" /* returning keys of updated tuples */
+ ") INSERT INTO %s (%s) " /* insert a new tuple if this doesn't exist */
+ "SELECT %s FROM %s AS diff "
+ "WHERE NOT EXISTS (SELECT 1 FROM updt AS mv WHERE %s);",
+ matviewname, count_colname, count_colname, count_colname,
+ deltaname_new,
+ match_cond,
+ returning_keys.data,
+ matviewname, target_list->data,
+ target_list->data, deltaname_new,
+ match_cond);
+
+ if (SPI_exec(querybuf.data, 0) != SPI_OK_INSERT)
+ elog(ERROR, "SPI_exec failed: %s", querybuf.data);
+}
+
/*
* apply_new_delta
*
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 6741e721ae..dad09b9b0b 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -56,6 +56,7 @@
#include "commands/cluster.h"
#include "commands/comment.h"
#include "commands/defrem.h"
+#include "commands/matview.h"
#include "commands/event_trigger.h"
#include "commands/sequence.h"
#include "commands/tablecmds.h"
@@ -3789,6 +3790,14 @@ renameatt_internal(Oid myrelid,
targetrelation = relation_open(myrelid, AccessExclusiveLock);
renameatt_check(myrelid, RelationGetForm(targetrelation), recursing);
+ /*
+ * Don't rename IVM columns.
+ */
+ if (RelationIsIVM(targetrelation) && isIvmName(oldattname))
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("IVM column can not be renamed")));
+
/*
* if the 'recurse' flag is set then we are supposed to rename this
* attribute in all classes that inherit from 'relname' (as well as in
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index 3337b77ae6..c191f70a6f 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -510,6 +510,7 @@ _outRangeTblEntry(StringInfo str, const RangeTblEntry *node)
WRITE_INT_FIELD(rellockmode);
WRITE_UINT_FIELD(perminfoindex);
WRITE_NODE_FIELD(tablesample);
+ WRITE_BOOL_FIELD(relisivm);
break;
case RTE_SUBQUERY:
WRITE_NODE_FIELD(subquery);
diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c
index c4d01a441a..ffcab8cda2 100644
--- a/src/backend/nodes/readfuncs.c
+++ b/src/backend/nodes/readfuncs.c
@@ -361,6 +361,7 @@ _readRangeTblEntry(void)
READ_INT_FIELD(rellockmode);
READ_UINT_FIELD(perminfoindex);
READ_NODE_FIELD(tablesample);
+ READ_BOOL_FIELD(relisivm);
break;
case RTE_SUBQUERY:
READ_NODE_FIELD(subquery);
diff --git a/src/backend/parser/parse_relation.c b/src/backend/parser/parse_relation.c
index 427b7325db..65aecc96a7 100644
--- a/src/backend/parser/parse_relation.c
+++ b/src/backend/parser/parse_relation.c
@@ -36,6 +36,7 @@
#include "utils/rel.h"
#include "utils/syscache.h"
#include "utils/varlena.h"
+#include "commands/matview.h"
/*
@@ -97,7 +98,7 @@ static void expandTupleDesc(TupleDesc tupdesc, Alias *eref,
int count, int offset,
int rtindex, int sublevels_up,
int location, bool include_dropped,
- List **colnames, List **colvars);
+ List **colnames, List **colvars, bool is_ivm);
static int specialAttNum(const char *attname);
static bool rte_visible_if_lateral(ParseState *pstate, RangeTblEntry *rte);
static bool rte_visible_if_qualified(ParseState *pstate, RangeTblEntry *rte);
@@ -1503,6 +1504,7 @@ addRangeTableEntry(ParseState *pstate,
rte->inh = inh;
rte->relkind = rel->rd_rel->relkind;
rte->rellockmode = lockmode;
+ rte->relisivm = rel->rd_rel->relisivm;
/*
* Build the list of effective column names using user-supplied aliases
@@ -1588,6 +1590,7 @@ addRangeTableEntryForRelation(ParseState *pstate,
rte->inh = inh;
rte->relkind = rel->rd_rel->relkind;
rte->rellockmode = lockmode;
+ rte->relisivm = rel->rd_rel->relisivm;
/*
* Build the list of effective column names using user-supplied aliases
@@ -2752,7 +2755,7 @@ expandRTE(RangeTblEntry *rte, int rtindex, int sublevels_up,
expandTupleDesc(tupdesc, rte->eref,
rtfunc->funccolcount, atts_done,
rtindex, sublevels_up, location,
- include_dropped, colnames, colvars);
+ include_dropped, colnames, colvars, false);
}
else if (functypclass == TYPEFUNC_SCALAR)
{
@@ -3020,7 +3023,7 @@ expandRelation(Oid relid, Alias *eref, int rtindex, int sublevels_up,
expandTupleDesc(rel->rd_att, eref, rel->rd_att->natts, 0,
rtindex, sublevels_up,
location, include_dropped,
- colnames, colvars);
+ colnames, colvars, RelationIsIVM(rel));
relation_close(rel, AccessShareLock);
}
@@ -3037,7 +3040,7 @@ static void
expandTupleDesc(TupleDesc tupdesc, Alias *eref, int count, int offset,
int rtindex, int sublevels_up,
int location, bool include_dropped,
- List **colnames, List **colvars)
+ List **colnames, List **colvars, bool is_ivm)
{
ListCell *aliascell;
int varattno;
@@ -3050,6 +3053,9 @@ expandTupleDesc(TupleDesc tupdesc, Alias *eref, int count, int offset,
{
Form_pg_attribute attr = TupleDescAttr(tupdesc, varattno);
+ if (is_ivm && isIvmName(NameStr(attr->attname)) && !MatViewIncrementalMaintenanceIsEnabled())
+ continue;
+
if (attr->attisdropped)
{
if (include_dropped)
@@ -3212,6 +3218,10 @@ expandNSItemAttrs(ParseState *pstate, ParseNamespaceItem *nsitem,
Var *varnode = (Var *) lfirst(var);
TargetEntry *te;
+ /* if transform * into columnlist with IMMV, remove IVM columns */
+ if (rte->relisivm && isIvmName(label) && !MatViewIncrementalMaintenanceIsEnabled())
+ continue;
+
te = makeTargetEntry((Expr *) varnode,
(AttrNumber) pstate->p_next_resno++,
label,
diff --git a/src/backend/rewrite/rewriteDefine.c b/src/backend/rewrite/rewriteDefine.c
index 6cc9a8d8bf..5d22dbcfcf 100644
--- a/src/backend/rewrite/rewriteDefine.c
+++ b/src/backend/rewrite/rewriteDefine.c
@@ -614,7 +614,8 @@ checkRuleResultList(List *targetList, TupleDesc resultDesc, bool isSelect,
attr->atttypmod))));
}
- if (i != resultDesc->natts)
+ /* No check for materialized views since this could have special columns for IVM */
+ if ((!isSelect || requireColumnNameMatch) && i != resultDesc->natts)
ereport(ERROR,
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
isSelect ?
diff --git a/src/include/commands/createas.h b/src/include/commands/createas.h
index 396ad1bb4c..6b47e66bfd 100644
--- a/src/include/commands/createas.h
+++ b/src/include/commands/createas.h
@@ -29,6 +29,8 @@ extern ObjectAddress ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *st
extern void CreateIvmTriggersOnBaseTables(Query *qry, Oid matviewOid);
extern void CreateIndexOnIMMV(Query *query, Relation matviewRel);
+extern Query *rewriteQueryForIMMV(Query *query, List *colNames);
+
extern int GetIntoRelEFlags(IntoClause *intoClause);
extern DestReceiver *CreateIntoRelDestReceiver(IntoClause *intoClause);
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index b89baef95d..bfa48d659e 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -1089,6 +1089,8 @@ typedef struct RangeTblEntry
Index perminfoindex pg_node_attr(query_jumble_ignore);
/* sampling info, or NULL */
struct TableSampleClause *tablesample;
+ /* incrementally maintainable materialized view? */
+ bool relisivm;
/*
* Fields valid for a subquery RTE (else NULL):
--
2.25.1
--Multipart=_Fri__29_Mar_2024_23_47_00_+0900_KGpmmDOIs1266Ib1
Content-Type: text/x-diff;
name="v31-0008-Add-aggregates-support-in-IVM.patch"
Content-Disposition: attachment;
filename="v31-0008-Add-aggregates-support-in-IVM.patch"
Content-Transfer-Encoding: quoted-printable
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v1] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 2 +
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 160 insertions(+), 1 deletion(-)
26.9% doc/src/sgml/
29.1% src/backend/utils/activity/
12.3% src/backend/utils/adt/
9.7% src/include/catalog/
4.0% src/include/utils/
9.2% src/test/regress/expected/
7.3% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index fa78031ccbb..ef89683164f 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..7cec0c83071 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,13 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static int pgStatBackendXactCommit = 0;
+static int pgStatBackendXactRollback = 0;
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +266,33 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += pgStatBackendXactCommit;
+ shbackendent->stats.xact_rollback += pgStatBackendXactRollback;
+
+ pgStatBackendXactCommit = pgStatBackendXactRollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +317,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +336,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +441,22 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ pgStatBackendXactCommit++;
+ else
+ pgStatBackendXactRollback++;
+
+ backend_has_xactstats = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..4c9f6b0dcec 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--ZnbSmJtA6EgjuGCe--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v2] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 166 insertions(+), 1 deletion(-)
26.1% doc/src/sgml/
29.1% src/backend/utils/activity/
11.9% src/backend/utils/adt/
9.4% src/include/catalog/
3.9% src/include/utils/
3.3% src/include/
8.9% src/test/regress/expected/
7.1% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 3f4a27a736e..77b41e02b82 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..12ae9a8d321 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,23 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..d3491faaff2 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Xact statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--SywurcztURg2uk+W--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v3 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. A new function is called in AtEOXact_PgStat() to increment those
two new counters.
---
src/backend/utils/activity/pgstat_backend.c | 57 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
4 files changed, 69 insertions(+), 1 deletion(-)
78.9% src/backend/utils/activity/
11.2% src/include/utils/
9.8% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..bf164854c4b 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,20 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /* Count transaction commit or abort */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..9efc0e10ebf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
--
2.34.1
--u6+Li7FU79Mt/lVd
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v3-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v4 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..47ce61e5093 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -36,7 +36,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index b31f20d41bc..400a8c5d734 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -342,6 +342,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..0fdbaf79780 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -801,6 +809,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..b97184c6539 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,7 +616,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--jg9kqX3osLGO+8A7
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v4-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v5 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 7727fed3bda..42bfb9cd38f 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -37,7 +37,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -48,6 +48,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -261,6 +266,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -285,6 +318,10 @@ pgstat_flush_backend(bool nowait, uint32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -300,6 +337,9 @@ pgstat_flush_backend(bool nowait, uint32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index 933dcb5cae5..218ff5e653c 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -352,6 +352,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 8e3549c3752..5d73c4dfbcf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -523,6 +523,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -535,6 +537,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -844,6 +852,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index eed4c6b359c..8077c65e938 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -707,7 +707,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, uint32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--uN5gGi2rtCNSVbpC
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v5-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v1] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 2 +
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 160 insertions(+), 1 deletion(-)
26.9% doc/src/sgml/
29.1% src/backend/utils/activity/
12.3% src/backend/utils/adt/
9.7% src/include/catalog/
4.0% src/include/utils/
9.2% src/test/regress/expected/
7.3% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index fa78031ccbb..ef89683164f 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..7cec0c83071 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,13 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static int pgStatBackendXactCommit = 0;
+static int pgStatBackendXactRollback = 0;
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +266,33 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += pgStatBackendXactCommit;
+ shbackendent->stats.xact_rollback += pgStatBackendXactRollback;
+
+ pgStatBackendXactCommit = pgStatBackendXactRollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +317,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +336,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +441,22 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ pgStatBackendXactCommit++;
+ else
+ pgStatBackendXactRollback++;
+
+ backend_has_xactstats = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..4c9f6b0dcec 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--ZnbSmJtA6EgjuGCe--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v2] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 166 insertions(+), 1 deletion(-)
26.1% doc/src/sgml/
29.1% src/backend/utils/activity/
11.9% src/backend/utils/adt/
9.4% src/include/catalog/
3.9% src/include/utils/
3.3% src/include/
8.9% src/test/regress/expected/
7.1% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 3f4a27a736e..77b41e02b82 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..12ae9a8d321 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,23 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..d3491faaff2 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Xact statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--SywurcztURg2uk+W--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v3 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. A new function is called in AtEOXact_PgStat() to increment those
two new counters.
---
src/backend/utils/activity/pgstat_backend.c | 57 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
4 files changed, 69 insertions(+), 1 deletion(-)
78.9% src/backend/utils/activity/
11.2% src/include/utils/
9.8% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..bf164854c4b 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,20 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /* Count transaction commit or abort */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..9efc0e10ebf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
--
2.34.1
--u6+Li7FU79Mt/lVd
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v3-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v4 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..47ce61e5093 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -36,7 +36,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index b31f20d41bc..400a8c5d734 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -342,6 +342,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..0fdbaf79780 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -801,6 +809,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..b97184c6539 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,7 +616,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--jg9kqX3osLGO+8A7
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v4-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v5 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 7727fed3bda..42bfb9cd38f 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -37,7 +37,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -48,6 +48,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -261,6 +266,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -285,6 +318,10 @@ pgstat_flush_backend(bool nowait, uint32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -300,6 +337,9 @@ pgstat_flush_backend(bool nowait, uint32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index 933dcb5cae5..218ff5e653c 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -352,6 +352,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 8e3549c3752..5d73c4dfbcf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -523,6 +523,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -535,6 +537,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -844,6 +852,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index eed4c6b359c..8077c65e938 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -707,7 +707,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, uint32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--uN5gGi2rtCNSVbpC
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v5-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v1] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 2 +
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 160 insertions(+), 1 deletion(-)
26.9% doc/src/sgml/
29.1% src/backend/utils/activity/
12.3% src/backend/utils/adt/
9.7% src/include/catalog/
4.0% src/include/utils/
9.2% src/test/regress/expected/
7.3% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index fa78031ccbb..ef89683164f 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..7cec0c83071 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,13 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static int pgStatBackendXactCommit = 0;
+static int pgStatBackendXactRollback = 0;
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +266,33 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += pgStatBackendXactCommit;
+ shbackendent->stats.xact_rollback += pgStatBackendXactRollback;
+
+ pgStatBackendXactCommit = pgStatBackendXactRollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +317,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +336,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +441,22 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ pgStatBackendXactCommit++;
+ else
+ pgStatBackendXactRollback++;
+
+ backend_has_xactstats = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..4c9f6b0dcec 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--ZnbSmJtA6EgjuGCe--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v2] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 166 insertions(+), 1 deletion(-)
26.1% doc/src/sgml/
29.1% src/backend/utils/activity/
11.9% src/backend/utils/adt/
9.4% src/include/catalog/
3.9% src/include/utils/
3.3% src/include/
8.9% src/test/regress/expected/
7.1% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 3f4a27a736e..77b41e02b82 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..12ae9a8d321 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,23 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..d3491faaff2 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Xact statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--SywurcztURg2uk+W--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v3 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. A new function is called in AtEOXact_PgStat() to increment those
two new counters.
---
src/backend/utils/activity/pgstat_backend.c | 57 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
4 files changed, 69 insertions(+), 1 deletion(-)
78.9% src/backend/utils/activity/
11.2% src/include/utils/
9.8% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..bf164854c4b 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,20 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /* Count transaction commit or abort */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..9efc0e10ebf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
--
2.34.1
--u6+Li7FU79Mt/lVd
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v3-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v4 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..47ce61e5093 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -36,7 +36,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index b31f20d41bc..400a8c5d734 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -342,6 +342,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..0fdbaf79780 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -801,6 +809,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..b97184c6539 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,7 +616,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--jg9kqX3osLGO+8A7
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v4-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v5 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 7727fed3bda..42bfb9cd38f 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -37,7 +37,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -48,6 +48,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -261,6 +266,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -285,6 +318,10 @@ pgstat_flush_backend(bool nowait, uint32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -300,6 +337,9 @@ pgstat_flush_backend(bool nowait, uint32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index 933dcb5cae5..218ff5e653c 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -352,6 +352,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 8e3549c3752..5d73c4dfbcf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -523,6 +523,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -535,6 +537,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -844,6 +852,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index eed4c6b359c..8077c65e938 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -707,7 +707,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, uint32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--uN5gGi2rtCNSVbpC
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v5-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v1] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 2 +
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 160 insertions(+), 1 deletion(-)
26.9% doc/src/sgml/
29.1% src/backend/utils/activity/
12.3% src/backend/utils/adt/
9.7% src/include/catalog/
4.0% src/include/utils/
9.2% src/test/regress/expected/
7.3% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index fa78031ccbb..ef89683164f 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..7cec0c83071 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,13 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static int pgStatBackendXactCommit = 0;
+static int pgStatBackendXactRollback = 0;
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +266,33 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += pgStatBackendXactCommit;
+ shbackendent->stats.xact_rollback += pgStatBackendXactRollback;
+
+ pgStatBackendXactCommit = pgStatBackendXactRollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +317,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +336,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +441,22 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ pgStatBackendXactCommit++;
+ else
+ pgStatBackendXactRollback++;
+
+ backend_has_xactstats = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..4c9f6b0dcec 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--ZnbSmJtA6EgjuGCe--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v4 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..47ce61e5093 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -36,7 +36,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index b31f20d41bc..400a8c5d734 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -342,6 +342,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..0fdbaf79780 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -801,6 +809,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..b97184c6539 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,7 +616,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--jg9kqX3osLGO+8A7
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v4-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v5 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 7727fed3bda..42bfb9cd38f 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -37,7 +37,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -48,6 +48,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -261,6 +266,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -285,6 +318,10 @@ pgstat_flush_backend(bool nowait, uint32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -300,6 +337,9 @@ pgstat_flush_backend(bool nowait, uint32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index 933dcb5cae5..218ff5e653c 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -352,6 +352,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 8e3549c3752..5d73c4dfbcf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -523,6 +523,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -535,6 +537,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -844,6 +852,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index eed4c6b359c..8077c65e938 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -707,7 +707,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, uint32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--uN5gGi2rtCNSVbpC
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v5-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v1] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 2 +
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 160 insertions(+), 1 deletion(-)
26.9% doc/src/sgml/
29.1% src/backend/utils/activity/
12.3% src/backend/utils/adt/
9.7% src/include/catalog/
4.0% src/include/utils/
9.2% src/test/regress/expected/
7.3% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index fa78031ccbb..ef89683164f 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..7cec0c83071 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,13 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static int pgStatBackendXactCommit = 0;
+static int pgStatBackendXactRollback = 0;
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +266,33 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += pgStatBackendXactCommit;
+ shbackendent->stats.xact_rollback += pgStatBackendXactRollback;
+
+ pgStatBackendXactCommit = pgStatBackendXactRollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +317,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +336,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +441,22 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ pgStatBackendXactCommit++;
+ else
+ pgStatBackendXactRollback++;
+
+ backend_has_xactstats = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..4c9f6b0dcec 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--ZnbSmJtA6EgjuGCe--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v2] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 166 insertions(+), 1 deletion(-)
26.1% doc/src/sgml/
29.1% src/backend/utils/activity/
11.9% src/backend/utils/adt/
9.4% src/include/catalog/
3.9% src/include/utils/
3.3% src/include/
8.9% src/test/regress/expected/
7.1% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 3f4a27a736e..77b41e02b82 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..12ae9a8d321 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,23 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..d3491faaff2 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Xact statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--SywurcztURg2uk+W--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v3 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. A new function is called in AtEOXact_PgStat() to increment those
two new counters.
---
src/backend/utils/activity/pgstat_backend.c | 57 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
4 files changed, 69 insertions(+), 1 deletion(-)
78.9% src/backend/utils/activity/
11.2% src/include/utils/
9.8% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..bf164854c4b 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,20 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /* Count transaction commit or abort */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..9efc0e10ebf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
--
2.34.1
--u6+Li7FU79Mt/lVd
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v3-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v4 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..47ce61e5093 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -36,7 +36,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index b31f20d41bc..400a8c5d734 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -342,6 +342,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..0fdbaf79780 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -801,6 +809,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..b97184c6539 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,7 +616,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--jg9kqX3osLGO+8A7
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v4-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v5 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 7727fed3bda..42bfb9cd38f 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -37,7 +37,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -48,6 +48,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -261,6 +266,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -285,6 +318,10 @@ pgstat_flush_backend(bool nowait, uint32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -300,6 +337,9 @@ pgstat_flush_backend(bool nowait, uint32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index 933dcb5cae5..218ff5e653c 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -352,6 +352,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 8e3549c3752..5d73c4dfbcf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -523,6 +523,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -535,6 +537,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -844,6 +852,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index eed4c6b359c..8077c65e938 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -707,7 +707,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, uint32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--uN5gGi2rtCNSVbpC
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v5-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v3 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. A new function is called in AtEOXact_PgStat() to increment those
two new counters.
---
src/backend/utils/activity/pgstat_backend.c | 57 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
4 files changed, 69 insertions(+), 1 deletion(-)
78.9% src/backend/utils/activity/
11.2% src/include/utils/
9.8% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..bf164854c4b 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,20 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /* Count transaction commit or abort */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..9efc0e10ebf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
--
2.34.1
--u6+Li7FU79Mt/lVd
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v3-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v5 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 7727fed3bda..42bfb9cd38f 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -37,7 +37,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -48,6 +48,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -261,6 +266,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -285,6 +318,10 @@ pgstat_flush_backend(bool nowait, uint32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -300,6 +337,9 @@ pgstat_flush_backend(bool nowait, uint32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index 933dcb5cae5..218ff5e653c 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -352,6 +352,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 8e3549c3752..5d73c4dfbcf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -523,6 +523,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -535,6 +537,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -844,6 +852,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index eed4c6b359c..8077c65e938 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -707,7 +707,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, uint32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--uN5gGi2rtCNSVbpC
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v5-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v1] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 2 +
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 160 insertions(+), 1 deletion(-)
26.9% doc/src/sgml/
29.1% src/backend/utils/activity/
12.3% src/backend/utils/adt/
9.7% src/include/catalog/
4.0% src/include/utils/
9.2% src/test/regress/expected/
7.3% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index fa78031ccbb..ef89683164f 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..7cec0c83071 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,13 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static int pgStatBackendXactCommit = 0;
+static int pgStatBackendXactRollback = 0;
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +266,33 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += pgStatBackendXactCommit;
+ shbackendent->stats.xact_rollback += pgStatBackendXactRollback;
+
+ pgStatBackendXactCommit = pgStatBackendXactRollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +317,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +336,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +441,22 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ pgStatBackendXactCommit++;
+ else
+ pgStatBackendXactRollback++;
+
+ backend_has_xactstats = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..4c9f6b0dcec 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--ZnbSmJtA6EgjuGCe--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v2] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 166 insertions(+), 1 deletion(-)
26.1% doc/src/sgml/
29.1% src/backend/utils/activity/
11.9% src/backend/utils/adt/
9.4% src/include/catalog/
3.9% src/include/utils/
3.3% src/include/
8.9% src/test/regress/expected/
7.1% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 3f4a27a736e..77b41e02b82 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..12ae9a8d321 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,23 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..d3491faaff2 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Xact statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--SywurcztURg2uk+W--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v3 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. A new function is called in AtEOXact_PgStat() to increment those
two new counters.
---
src/backend/utils/activity/pgstat_backend.c | 57 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
4 files changed, 69 insertions(+), 1 deletion(-)
78.9% src/backend/utils/activity/
11.2% src/include/utils/
9.8% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..bf164854c4b 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,20 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /* Count transaction commit or abort */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..9efc0e10ebf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
--
2.34.1
--u6+Li7FU79Mt/lVd
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v3-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v4 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..47ce61e5093 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -36,7 +36,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index b31f20d41bc..400a8c5d734 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -342,6 +342,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..0fdbaf79780 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -801,6 +809,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..b97184c6539 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,7 +616,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--jg9kqX3osLGO+8A7
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v4-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v5 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 7727fed3bda..42bfb9cd38f 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -37,7 +37,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -48,6 +48,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -261,6 +266,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -285,6 +318,10 @@ pgstat_flush_backend(bool nowait, uint32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -300,6 +337,9 @@ pgstat_flush_backend(bool nowait, uint32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index 933dcb5cae5..218ff5e653c 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -352,6 +352,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 8e3549c3752..5d73c4dfbcf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -523,6 +523,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -535,6 +537,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -844,6 +852,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index eed4c6b359c..8077c65e938 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -707,7 +707,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, uint32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--uN5gGi2rtCNSVbpC
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v5-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v1] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 2 +
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 160 insertions(+), 1 deletion(-)
26.9% doc/src/sgml/
29.1% src/backend/utils/activity/
12.3% src/backend/utils/adt/
9.7% src/include/catalog/
4.0% src/include/utils/
9.2% src/test/regress/expected/
7.3% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index fa78031ccbb..ef89683164f 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..7cec0c83071 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,13 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static int pgStatBackendXactCommit = 0;
+static int pgStatBackendXactRollback = 0;
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +266,33 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += pgStatBackendXactCommit;
+ shbackendent->stats.xact_rollback += pgStatBackendXactRollback;
+
+ pgStatBackendXactCommit = pgStatBackendXactRollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +317,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +336,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +441,22 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ pgStatBackendXactCommit++;
+ else
+ pgStatBackendXactRollback++;
+
+ backend_has_xactstats = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..4c9f6b0dcec 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--ZnbSmJtA6EgjuGCe--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v2] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 166 insertions(+), 1 deletion(-)
26.1% doc/src/sgml/
29.1% src/backend/utils/activity/
11.9% src/backend/utils/adt/
9.4% src/include/catalog/
3.9% src/include/utils/
3.3% src/include/
8.9% src/test/regress/expected/
7.1% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 3f4a27a736e..77b41e02b82 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..12ae9a8d321 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,23 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..d3491faaff2 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Xact statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--SywurcztURg2uk+W--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v3 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. A new function is called in AtEOXact_PgStat() to increment those
two new counters.
---
src/backend/utils/activity/pgstat_backend.c | 57 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
4 files changed, 69 insertions(+), 1 deletion(-)
78.9% src/backend/utils/activity/
11.2% src/include/utils/
9.8% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..bf164854c4b 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,20 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /* Count transaction commit or abort */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..9efc0e10ebf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
--
2.34.1
--u6+Li7FU79Mt/lVd
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v3-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v1] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 2 +
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 160 insertions(+), 1 deletion(-)
26.9% doc/src/sgml/
29.1% src/backend/utils/activity/
12.3% src/backend/utils/adt/
9.7% src/include/catalog/
4.0% src/include/utils/
9.2% src/test/regress/expected/
7.3% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index fa78031ccbb..ef89683164f 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..7cec0c83071 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,13 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static int pgStatBackendXactCommit = 0;
+static int pgStatBackendXactRollback = 0;
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +266,33 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += pgStatBackendXactCommit;
+ shbackendent->stats.xact_rollback += pgStatBackendXactRollback;
+
+ pgStatBackendXactCommit = pgStatBackendXactRollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +317,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +336,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +441,22 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ pgStatBackendXactCommit++;
+ else
+ pgStatBackendXactRollback++;
+
+ backend_has_xactstats = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..4c9f6b0dcec 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--ZnbSmJtA6EgjuGCe--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v2] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 166 insertions(+), 1 deletion(-)
26.1% doc/src/sgml/
29.1% src/backend/utils/activity/
11.9% src/backend/utils/adt/
9.4% src/include/catalog/
3.9% src/include/utils/
3.3% src/include/
8.9% src/test/regress/expected/
7.1% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 3f4a27a736e..77b41e02b82 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..12ae9a8d321 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,23 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..d3491faaff2 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Xact statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--SywurcztURg2uk+W--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v3 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. A new function is called in AtEOXact_PgStat() to increment those
two new counters.
---
src/backend/utils/activity/pgstat_backend.c | 57 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
4 files changed, 69 insertions(+), 1 deletion(-)
78.9% src/backend/utils/activity/
11.2% src/include/utils/
9.8% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..bf164854c4b 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,20 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /* Count transaction commit or abort */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..9efc0e10ebf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
--
2.34.1
--u6+Li7FU79Mt/lVd
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v3-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v4 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..47ce61e5093 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -36,7 +36,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index b31f20d41bc..400a8c5d734 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -342,6 +342,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..0fdbaf79780 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -801,6 +809,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..b97184c6539 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,7 +616,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--jg9kqX3osLGO+8A7
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v4-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v5 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 7727fed3bda..42bfb9cd38f 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -37,7 +37,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -48,6 +48,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -261,6 +266,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -285,6 +318,10 @@ pgstat_flush_backend(bool nowait, uint32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -300,6 +337,9 @@ pgstat_flush_backend(bool nowait, uint32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index 933dcb5cae5..218ff5e653c 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -352,6 +352,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 8e3549c3752..5d73c4dfbcf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -523,6 +523,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -535,6 +537,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -844,6 +852,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index eed4c6b359c..8077c65e938 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -707,7 +707,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, uint32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--uN5gGi2rtCNSVbpC
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v5-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v4 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..47ce61e5093 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -36,7 +36,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index b31f20d41bc..400a8c5d734 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -342,6 +342,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..0fdbaf79780 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -801,6 +809,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..b97184c6539 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,7 +616,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--jg9kqX3osLGO+8A7
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v4-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v2] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 166 insertions(+), 1 deletion(-)
26.1% doc/src/sgml/
29.1% src/backend/utils/activity/
11.9% src/backend/utils/adt/
9.4% src/include/catalog/
3.9% src/include/utils/
3.3% src/include/
8.9% src/test/regress/expected/
7.1% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 3f4a27a736e..77b41e02b82 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..12ae9a8d321 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,23 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..d3491faaff2 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Xact statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--SywurcztURg2uk+W--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v3 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. A new function is called in AtEOXact_PgStat() to increment those
two new counters.
---
src/backend/utils/activity/pgstat_backend.c | 57 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
4 files changed, 69 insertions(+), 1 deletion(-)
78.9% src/backend/utils/activity/
11.2% src/include/utils/
9.8% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..bf164854c4b 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,20 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /* Count transaction commit or abort */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..9efc0e10ebf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
--
2.34.1
--u6+Li7FU79Mt/lVd
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v3-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v4 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..47ce61e5093 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -36,7 +36,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index b31f20d41bc..400a8c5d734 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -342,6 +342,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..0fdbaf79780 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -801,6 +809,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..b97184c6539 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,7 +616,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--jg9kqX3osLGO+8A7
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v4-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v5 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 7727fed3bda..42bfb9cd38f 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -37,7 +37,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -48,6 +48,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -261,6 +266,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -285,6 +318,10 @@ pgstat_flush_backend(bool nowait, uint32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -300,6 +337,9 @@ pgstat_flush_backend(bool nowait, uint32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index 933dcb5cae5..218ff5e653c 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -352,6 +352,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 8e3549c3752..5d73c4dfbcf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -523,6 +523,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -535,6 +537,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -844,6 +852,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index eed4c6b359c..8077c65e938 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -707,7 +707,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, uint32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--uN5gGi2rtCNSVbpC
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v5-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v1] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 2 +
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 160 insertions(+), 1 deletion(-)
26.9% doc/src/sgml/
29.1% src/backend/utils/activity/
12.3% src/backend/utils/adt/
9.7% src/include/catalog/
4.0% src/include/utils/
9.2% src/test/regress/expected/
7.3% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index fa78031ccbb..ef89683164f 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..7cec0c83071 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,13 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static int pgStatBackendXactCommit = 0;
+static int pgStatBackendXactRollback = 0;
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +266,33 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += pgStatBackendXactCommit;
+ shbackendent->stats.xact_rollback += pgStatBackendXactRollback;
+
+ pgStatBackendXactCommit = pgStatBackendXactRollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +317,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +336,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +441,22 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ pgStatBackendXactCommit++;
+ else
+ pgStatBackendXactRollback++;
+
+ backend_has_xactstats = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..4c9f6b0dcec 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--ZnbSmJtA6EgjuGCe--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v2] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 166 insertions(+), 1 deletion(-)
26.1% doc/src/sgml/
29.1% src/backend/utils/activity/
11.9% src/backend/utils/adt/
9.4% src/include/catalog/
3.9% src/include/utils/
3.3% src/include/
8.9% src/test/regress/expected/
7.1% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 3f4a27a736e..77b41e02b82 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..12ae9a8d321 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,23 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..d3491faaff2 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Xact statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--SywurcztURg2uk+W--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v3 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. A new function is called in AtEOXact_PgStat() to increment those
two new counters.
---
src/backend/utils/activity/pgstat_backend.c | 57 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
4 files changed, 69 insertions(+), 1 deletion(-)
78.9% src/backend/utils/activity/
11.2% src/include/utils/
9.8% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..bf164854c4b 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,20 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /* Count transaction commit or abort */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..9efc0e10ebf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
--
2.34.1
--u6+Li7FU79Mt/lVd
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v3-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v4 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..47ce61e5093 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -36,7 +36,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index b31f20d41bc..400a8c5d734 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -342,6 +342,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..0fdbaf79780 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -801,6 +809,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..b97184c6539 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,7 +616,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--jg9kqX3osLGO+8A7
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v4-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v5 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 7727fed3bda..42bfb9cd38f 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -37,7 +37,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -48,6 +48,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -261,6 +266,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -285,6 +318,10 @@ pgstat_flush_backend(bool nowait, uint32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -300,6 +337,9 @@ pgstat_flush_backend(bool nowait, uint32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index 933dcb5cae5..218ff5e653c 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -352,6 +352,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 8e3549c3752..5d73c4dfbcf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -523,6 +523,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -535,6 +537,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -844,6 +852,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index eed4c6b359c..8077c65e938 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -707,7 +707,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, uint32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--uN5gGi2rtCNSVbpC
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v5-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v4 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..47ce61e5093 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -36,7 +36,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index b31f20d41bc..400a8c5d734 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -342,6 +342,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..0fdbaf79780 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -801,6 +809,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..b97184c6539 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,7 +616,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--jg9kqX3osLGO+8A7
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v4-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v5 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 7727fed3bda..42bfb9cd38f 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -37,7 +37,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -48,6 +48,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -261,6 +266,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -285,6 +318,10 @@ pgstat_flush_backend(bool nowait, uint32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -300,6 +337,9 @@ pgstat_flush_backend(bool nowait, uint32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index 933dcb5cae5..218ff5e653c 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -352,6 +352,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 8e3549c3752..5d73c4dfbcf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -523,6 +523,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -535,6 +537,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -844,6 +852,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index eed4c6b359c..8077c65e938 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -707,7 +707,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, uint32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--uN5gGi2rtCNSVbpC
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v5-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v1] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 2 +
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 160 insertions(+), 1 deletion(-)
26.9% doc/src/sgml/
29.1% src/backend/utils/activity/
12.3% src/backend/utils/adt/
9.7% src/include/catalog/
4.0% src/include/utils/
9.2% src/test/regress/expected/
7.3% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index fa78031ccbb..ef89683164f 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..7cec0c83071 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,13 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static int pgStatBackendXactCommit = 0;
+static int pgStatBackendXactRollback = 0;
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +266,33 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += pgStatBackendXactCommit;
+ shbackendent->stats.xact_rollback += pgStatBackendXactRollback;
+
+ pgStatBackendXactCommit = pgStatBackendXactRollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +317,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +336,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +441,22 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ pgStatBackendXactCommit++;
+ else
+ pgStatBackendXactRollback++;
+
+ backend_has_xactstats = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..4c9f6b0dcec 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--ZnbSmJtA6EgjuGCe--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v2] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 166 insertions(+), 1 deletion(-)
26.1% doc/src/sgml/
29.1% src/backend/utils/activity/
11.9% src/backend/utils/adt/
9.4% src/include/catalog/
3.9% src/include/utils/
3.3% src/include/
8.9% src/test/regress/expected/
7.1% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 3f4a27a736e..77b41e02b82 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..12ae9a8d321 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,23 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..d3491faaff2 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Xact statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--SywurcztURg2uk+W--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v3 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. A new function is called in AtEOXact_PgStat() to increment those
two new counters.
---
src/backend/utils/activity/pgstat_backend.c | 57 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
4 files changed, 69 insertions(+), 1 deletion(-)
78.9% src/backend/utils/activity/
11.2% src/include/utils/
9.8% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..bf164854c4b 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,20 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /* Count transaction commit or abort */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..9efc0e10ebf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
--
2.34.1
--u6+Li7FU79Mt/lVd
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v3-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v4 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..47ce61e5093 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -36,7 +36,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index b31f20d41bc..400a8c5d734 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -342,6 +342,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..0fdbaf79780 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -801,6 +809,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..b97184c6539 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,7 +616,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--jg9kqX3osLGO+8A7
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v4-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v5 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 7727fed3bda..42bfb9cd38f 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -37,7 +37,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -48,6 +48,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -261,6 +266,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -285,6 +318,10 @@ pgstat_flush_backend(bool nowait, uint32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -300,6 +337,9 @@ pgstat_flush_backend(bool nowait, uint32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index 933dcb5cae5..218ff5e653c 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -352,6 +352,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 8e3549c3752..5d73c4dfbcf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -523,6 +523,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -535,6 +537,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -844,6 +852,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index eed4c6b359c..8077c65e938 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -707,7 +707,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, uint32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--uN5gGi2rtCNSVbpC
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v5-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v1] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 2 +
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 160 insertions(+), 1 deletion(-)
26.9% doc/src/sgml/
29.1% src/backend/utils/activity/
12.3% src/backend/utils/adt/
9.7% src/include/catalog/
4.0% src/include/utils/
9.2% src/test/regress/expected/
7.3% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index fa78031ccbb..ef89683164f 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..7cec0c83071 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,13 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static int pgStatBackendXactCommit = 0;
+static int pgStatBackendXactRollback = 0;
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +266,33 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += pgStatBackendXactCommit;
+ shbackendent->stats.xact_rollback += pgStatBackendXactRollback;
+
+ pgStatBackendXactCommit = pgStatBackendXactRollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +317,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +336,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +441,22 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ pgStatBackendXactCommit++;
+ else
+ pgStatBackendXactRollback++;
+
+ backend_has_xactstats = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..4c9f6b0dcec 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--ZnbSmJtA6EgjuGCe--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v2] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 166 insertions(+), 1 deletion(-)
26.1% doc/src/sgml/
29.1% src/backend/utils/activity/
11.9% src/backend/utils/adt/
9.4% src/include/catalog/
3.9% src/include/utils/
3.3% src/include/
8.9% src/test/regress/expected/
7.1% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 3f4a27a736e..77b41e02b82 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..12ae9a8d321 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,23 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..d3491faaff2 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Xact statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--SywurcztURg2uk+W--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v3 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. A new function is called in AtEOXact_PgStat() to increment those
two new counters.
---
src/backend/utils/activity/pgstat_backend.c | 57 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
4 files changed, 69 insertions(+), 1 deletion(-)
78.9% src/backend/utils/activity/
11.2% src/include/utils/
9.8% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..bf164854c4b 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,20 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /* Count transaction commit or abort */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..9efc0e10ebf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
--
2.34.1
--u6+Li7FU79Mt/lVd
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v3-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v4 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..47ce61e5093 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -36,7 +36,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index b31f20d41bc..400a8c5d734 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -342,6 +342,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..0fdbaf79780 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -801,6 +809,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..b97184c6539 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,7 +616,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--jg9kqX3osLGO+8A7
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v4-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v5 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 7727fed3bda..42bfb9cd38f 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -37,7 +37,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -48,6 +48,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -261,6 +266,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -285,6 +318,10 @@ pgstat_flush_backend(bool nowait, uint32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -300,6 +337,9 @@ pgstat_flush_backend(bool nowait, uint32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index 933dcb5cae5..218ff5e653c 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -352,6 +352,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 8e3549c3752..5d73c4dfbcf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -523,6 +523,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -535,6 +537,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -844,6 +852,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index eed4c6b359c..8077c65e938 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -707,7 +707,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, uint32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--uN5gGi2rtCNSVbpC
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v5-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v1] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 2 +
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 160 insertions(+), 1 deletion(-)
26.9% doc/src/sgml/
29.1% src/backend/utils/activity/
12.3% src/backend/utils/adt/
9.7% src/include/catalog/
4.0% src/include/utils/
9.2% src/test/regress/expected/
7.3% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index fa78031ccbb..ef89683164f 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..7cec0c83071 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,13 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static int pgStatBackendXactCommit = 0;
+static int pgStatBackendXactRollback = 0;
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +266,33 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += pgStatBackendXactCommit;
+ shbackendent->stats.xact_rollback += pgStatBackendXactRollback;
+
+ pgStatBackendXactCommit = pgStatBackendXactRollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +317,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +336,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +441,22 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ pgStatBackendXactCommit++;
+ else
+ pgStatBackendXactRollback++;
+
+ backend_has_xactstats = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..4c9f6b0dcec 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--ZnbSmJtA6EgjuGCe--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v2] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 166 insertions(+), 1 deletion(-)
26.1% doc/src/sgml/
29.1% src/backend/utils/activity/
11.9% src/backend/utils/adt/
9.4% src/include/catalog/
3.9% src/include/utils/
3.3% src/include/
8.9% src/test/regress/expected/
7.1% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 3f4a27a736e..77b41e02b82 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..12ae9a8d321 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,23 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..d3491faaff2 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Xact statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--SywurcztURg2uk+W--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v3 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. A new function is called in AtEOXact_PgStat() to increment those
two new counters.
---
src/backend/utils/activity/pgstat_backend.c | 57 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
4 files changed, 69 insertions(+), 1 deletion(-)
78.9% src/backend/utils/activity/
11.2% src/include/utils/
9.8% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..bf164854c4b 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,20 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /* Count transaction commit or abort */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..9efc0e10ebf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
--
2.34.1
--u6+Li7FU79Mt/lVd
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v3-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v4 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..47ce61e5093 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -36,7 +36,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index b31f20d41bc..400a8c5d734 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -342,6 +342,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..0fdbaf79780 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -801,6 +809,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..b97184c6539 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,7 +616,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--jg9kqX3osLGO+8A7
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v4-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v5 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 7727fed3bda..42bfb9cd38f 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -37,7 +37,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -48,6 +48,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -261,6 +266,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -285,6 +318,10 @@ pgstat_flush_backend(bool nowait, uint32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -300,6 +337,9 @@ pgstat_flush_backend(bool nowait, uint32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index 933dcb5cae5..218ff5e653c 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -352,6 +352,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 8e3549c3752..5d73c4dfbcf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -523,6 +523,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -535,6 +537,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -844,6 +852,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index eed4c6b359c..8077c65e938 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -707,7 +707,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, uint32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--uN5gGi2rtCNSVbpC
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v5-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v1] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 2 +
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 160 insertions(+), 1 deletion(-)
26.9% doc/src/sgml/
29.1% src/backend/utils/activity/
12.3% src/backend/utils/adt/
9.7% src/include/catalog/
4.0% src/include/utils/
9.2% src/test/regress/expected/
7.3% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index fa78031ccbb..ef89683164f 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..7cec0c83071 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,13 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static int pgStatBackendXactCommit = 0;
+static int pgStatBackendXactRollback = 0;
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +266,33 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += pgStatBackendXactCommit;
+ shbackendent->stats.xact_rollback += pgStatBackendXactRollback;
+
+ pgStatBackendXactCommit = pgStatBackendXactRollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +317,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +336,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +441,22 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ pgStatBackendXactCommit++;
+ else
+ pgStatBackendXactRollback++;
+
+ backend_has_xactstats = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..4c9f6b0dcec 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--ZnbSmJtA6EgjuGCe--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v2] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 166 insertions(+), 1 deletion(-)
26.1% doc/src/sgml/
29.1% src/backend/utils/activity/
11.9% src/backend/utils/adt/
9.4% src/include/catalog/
3.9% src/include/utils/
3.3% src/include/
8.9% src/test/regress/expected/
7.1% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 3f4a27a736e..77b41e02b82 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..12ae9a8d321 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,23 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..d3491faaff2 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Xact statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--SywurcztURg2uk+W--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v3 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. A new function is called in AtEOXact_PgStat() to increment those
two new counters.
---
src/backend/utils/activity/pgstat_backend.c | 57 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
4 files changed, 69 insertions(+), 1 deletion(-)
78.9% src/backend/utils/activity/
11.2% src/include/utils/
9.8% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..bf164854c4b 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,20 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /* Count transaction commit or abort */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..9efc0e10ebf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
--
2.34.1
--u6+Li7FU79Mt/lVd
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v3-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v1] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 2 +
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 160 insertions(+), 1 deletion(-)
26.9% doc/src/sgml/
29.1% src/backend/utils/activity/
12.3% src/backend/utils/adt/
9.7% src/include/catalog/
4.0% src/include/utils/
9.2% src/test/regress/expected/
7.3% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index fa78031ccbb..ef89683164f 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..7cec0c83071 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,13 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static int pgStatBackendXactCommit = 0;
+static int pgStatBackendXactRollback = 0;
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +266,33 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += pgStatBackendXactCommit;
+ shbackendent->stats.xact_rollback += pgStatBackendXactRollback;
+
+ pgStatBackendXactCommit = pgStatBackendXactRollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +317,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +336,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +441,22 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ pgStatBackendXactCommit++;
+ else
+ pgStatBackendXactRollback++;
+
+ backend_has_xactstats = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..4c9f6b0dcec 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--ZnbSmJtA6EgjuGCe--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v2] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 166 insertions(+), 1 deletion(-)
26.1% doc/src/sgml/
29.1% src/backend/utils/activity/
11.9% src/backend/utils/adt/
9.4% src/include/catalog/
3.9% src/include/utils/
3.3% src/include/
8.9% src/test/regress/expected/
7.1% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 3f4a27a736e..77b41e02b82 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..12ae9a8d321 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,23 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..d3491faaff2 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Xact statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--SywurcztURg2uk+W--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v3 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. A new function is called in AtEOXact_PgStat() to increment those
two new counters.
---
src/backend/utils/activity/pgstat_backend.c | 57 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
4 files changed, 69 insertions(+), 1 deletion(-)
78.9% src/backend/utils/activity/
11.2% src/include/utils/
9.8% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..bf164854c4b 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,20 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /* Count transaction commit or abort */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..9efc0e10ebf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
--
2.34.1
--u6+Li7FU79Mt/lVd
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v3-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v5 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 7727fed3bda..42bfb9cd38f 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -37,7 +37,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -48,6 +48,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -261,6 +266,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -285,6 +318,10 @@ pgstat_flush_backend(bool nowait, uint32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -300,6 +337,9 @@ pgstat_flush_backend(bool nowait, uint32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index 933dcb5cae5..218ff5e653c 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -352,6 +352,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 8e3549c3752..5d73c4dfbcf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -523,6 +523,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -535,6 +537,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -844,6 +852,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index eed4c6b359c..8077c65e938 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -707,7 +707,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, uint32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--uN5gGi2rtCNSVbpC
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v5-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v1] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 2 +
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 160 insertions(+), 1 deletion(-)
26.9% doc/src/sgml/
29.1% src/backend/utils/activity/
12.3% src/backend/utils/adt/
9.7% src/include/catalog/
4.0% src/include/utils/
9.2% src/test/regress/expected/
7.3% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index fa78031ccbb..ef89683164f 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..7cec0c83071 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,13 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static int pgStatBackendXactCommit = 0;
+static int pgStatBackendXactRollback = 0;
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +266,33 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += pgStatBackendXactCommit;
+ shbackendent->stats.xact_rollback += pgStatBackendXactRollback;
+
+ pgStatBackendXactCommit = pgStatBackendXactRollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +317,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +336,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +441,22 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ pgStatBackendXactCommit++;
+ else
+ pgStatBackendXactRollback++;
+
+ backend_has_xactstats = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..4c9f6b0dcec 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--ZnbSmJtA6EgjuGCe--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v2] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 166 insertions(+), 1 deletion(-)
26.1% doc/src/sgml/
29.1% src/backend/utils/activity/
11.9% src/backend/utils/adt/
9.4% src/include/catalog/
3.9% src/include/utils/
3.3% src/include/
8.9% src/test/regress/expected/
7.1% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 3f4a27a736e..77b41e02b82 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..12ae9a8d321 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,23 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..d3491faaff2 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Xact statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--SywurcztURg2uk+W--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v3 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. A new function is called in AtEOXact_PgStat() to increment those
two new counters.
---
src/backend/utils/activity/pgstat_backend.c | 57 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
4 files changed, 69 insertions(+), 1 deletion(-)
78.9% src/backend/utils/activity/
11.2% src/include/utils/
9.8% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..bf164854c4b 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,20 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /* Count transaction commit or abort */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..9efc0e10ebf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
--
2.34.1
--u6+Li7FU79Mt/lVd
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v3-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v4 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..47ce61e5093 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -36,7 +36,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index b31f20d41bc..400a8c5d734 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -342,6 +342,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..0fdbaf79780 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -801,6 +809,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..b97184c6539 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,7 +616,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--jg9kqX3osLGO+8A7
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v4-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v5 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 7727fed3bda..42bfb9cd38f 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -37,7 +37,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -48,6 +48,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -261,6 +266,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -285,6 +318,10 @@ pgstat_flush_backend(bool nowait, uint32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -300,6 +337,9 @@ pgstat_flush_backend(bool nowait, uint32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index 933dcb5cae5..218ff5e653c 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -352,6 +352,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 8e3549c3752..5d73c4dfbcf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -523,6 +523,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -535,6 +537,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -844,6 +852,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index eed4c6b359c..8077c65e938 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -707,7 +707,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, uint32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--uN5gGi2rtCNSVbpC
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v5-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v1] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 2 +
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 160 insertions(+), 1 deletion(-)
26.9% doc/src/sgml/
29.1% src/backend/utils/activity/
12.3% src/backend/utils/adt/
9.7% src/include/catalog/
4.0% src/include/utils/
9.2% src/test/regress/expected/
7.3% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index fa78031ccbb..ef89683164f 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..7cec0c83071 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,13 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static int pgStatBackendXactCommit = 0;
+static int pgStatBackendXactRollback = 0;
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +266,33 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += pgStatBackendXactCommit;
+ shbackendent->stats.xact_rollback += pgStatBackendXactRollback;
+
+ pgStatBackendXactCommit = pgStatBackendXactRollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +317,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +336,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +441,22 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ pgStatBackendXactCommit++;
+ else
+ pgStatBackendXactRollback++;
+
+ backend_has_xactstats = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..4c9f6b0dcec 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--ZnbSmJtA6EgjuGCe--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v2] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 166 insertions(+), 1 deletion(-)
26.1% doc/src/sgml/
29.1% src/backend/utils/activity/
11.9% src/backend/utils/adt/
9.4% src/include/catalog/
3.9% src/include/utils/
3.3% src/include/
8.9% src/test/regress/expected/
7.1% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 3f4a27a736e..77b41e02b82 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..12ae9a8d321 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,23 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..d3491faaff2 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Xact statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--SywurcztURg2uk+W--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v3 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. A new function is called in AtEOXact_PgStat() to increment those
two new counters.
---
src/backend/utils/activity/pgstat_backend.c | 57 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
4 files changed, 69 insertions(+), 1 deletion(-)
78.9% src/backend/utils/activity/
11.2% src/include/utils/
9.8% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..bf164854c4b 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,20 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /* Count transaction commit or abort */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..9efc0e10ebf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
--
2.34.1
--u6+Li7FU79Mt/lVd
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v3-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v4 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..47ce61e5093 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -36,7 +36,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index b31f20d41bc..400a8c5d734 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -342,6 +342,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..0fdbaf79780 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -801,6 +809,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..b97184c6539 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,7 +616,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--jg9kqX3osLGO+8A7
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v4-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v5 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 7727fed3bda..42bfb9cd38f 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -37,7 +37,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -48,6 +48,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -261,6 +266,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -285,6 +318,10 @@ pgstat_flush_backend(bool nowait, uint32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -300,6 +337,9 @@ pgstat_flush_backend(bool nowait, uint32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index 933dcb5cae5..218ff5e653c 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -352,6 +352,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 8e3549c3752..5d73c4dfbcf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -523,6 +523,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -535,6 +537,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -844,6 +852,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index eed4c6b359c..8077c65e938 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -707,7 +707,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, uint32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--uN5gGi2rtCNSVbpC
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v5-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v1] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 2 +
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 160 insertions(+), 1 deletion(-)
26.9% doc/src/sgml/
29.1% src/backend/utils/activity/
12.3% src/backend/utils/adt/
9.7% src/include/catalog/
4.0% src/include/utils/
9.2% src/test/regress/expected/
7.3% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index fa78031ccbb..ef89683164f 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..7cec0c83071 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,13 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static int pgStatBackendXactCommit = 0;
+static int pgStatBackendXactRollback = 0;
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +266,33 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += pgStatBackendXactCommit;
+ shbackendent->stats.xact_rollback += pgStatBackendXactRollback;
+
+ pgStatBackendXactCommit = pgStatBackendXactRollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +317,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +336,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +441,22 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ pgStatBackendXactCommit++;
+ else
+ pgStatBackendXactRollback++;
+
+ backend_has_xactstats = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..4c9f6b0dcec 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--ZnbSmJtA6EgjuGCe--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v2] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 166 insertions(+), 1 deletion(-)
26.1% doc/src/sgml/
29.1% src/backend/utils/activity/
11.9% src/backend/utils/adt/
9.4% src/include/catalog/
3.9% src/include/utils/
3.3% src/include/
8.9% src/test/regress/expected/
7.1% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 3f4a27a736e..77b41e02b82 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..12ae9a8d321 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,23 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..d3491faaff2 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Xact statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--SywurcztURg2uk+W--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v3 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. A new function is called in AtEOXact_PgStat() to increment those
two new counters.
---
src/backend/utils/activity/pgstat_backend.c | 57 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
4 files changed, 69 insertions(+), 1 deletion(-)
78.9% src/backend/utils/activity/
11.2% src/include/utils/
9.8% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..bf164854c4b 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,20 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /* Count transaction commit or abort */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..9efc0e10ebf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
--
2.34.1
--u6+Li7FU79Mt/lVd
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v3-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v4 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..47ce61e5093 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -36,7 +36,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index b31f20d41bc..400a8c5d734 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -342,6 +342,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..0fdbaf79780 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -801,6 +809,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..b97184c6539 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,7 +616,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--jg9kqX3osLGO+8A7
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v4-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v5 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 7727fed3bda..42bfb9cd38f 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -37,7 +37,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -48,6 +48,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -261,6 +266,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -285,6 +318,10 @@ pgstat_flush_backend(bool nowait, uint32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -300,6 +337,9 @@ pgstat_flush_backend(bool nowait, uint32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index 933dcb5cae5..218ff5e653c 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -352,6 +352,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 8e3549c3752..5d73c4dfbcf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -523,6 +523,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -535,6 +537,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -844,6 +852,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index eed4c6b359c..8077c65e938 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -707,7 +707,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, uint32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--uN5gGi2rtCNSVbpC
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v5-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v1] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 2 +
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 160 insertions(+), 1 deletion(-)
26.9% doc/src/sgml/
29.1% src/backend/utils/activity/
12.3% src/backend/utils/adt/
9.7% src/include/catalog/
4.0% src/include/utils/
9.2% src/test/regress/expected/
7.3% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index fa78031ccbb..ef89683164f 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..7cec0c83071 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,13 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static int pgStatBackendXactCommit = 0;
+static int pgStatBackendXactRollback = 0;
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +266,33 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += pgStatBackendXactCommit;
+ shbackendent->stats.xact_rollback += pgStatBackendXactRollback;
+
+ pgStatBackendXactCommit = pgStatBackendXactRollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +317,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +336,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +441,22 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ pgStatBackendXactCommit++;
+ else
+ pgStatBackendXactRollback++;
+
+ backend_has_xactstats = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..4c9f6b0dcec 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--ZnbSmJtA6EgjuGCe--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v2] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 166 insertions(+), 1 deletion(-)
26.1% doc/src/sgml/
29.1% src/backend/utils/activity/
11.9% src/backend/utils/adt/
9.4% src/include/catalog/
3.9% src/include/utils/
3.3% src/include/
8.9% src/test/regress/expected/
7.1% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 3f4a27a736e..77b41e02b82 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..12ae9a8d321 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,23 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..d3491faaff2 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Xact statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--SywurcztURg2uk+W--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v3 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. A new function is called in AtEOXact_PgStat() to increment those
two new counters.
---
src/backend/utils/activity/pgstat_backend.c | 57 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
4 files changed, 69 insertions(+), 1 deletion(-)
78.9% src/backend/utils/activity/
11.2% src/include/utils/
9.8% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..bf164854c4b 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,20 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /* Count transaction commit or abort */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..9efc0e10ebf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
--
2.34.1
--u6+Li7FU79Mt/lVd
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v3-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v4 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..47ce61e5093 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -36,7 +36,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index b31f20d41bc..400a8c5d734 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -342,6 +342,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..0fdbaf79780 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -801,6 +809,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..b97184c6539 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,7 +616,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--jg9kqX3osLGO+8A7
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v4-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v5 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 7727fed3bda..42bfb9cd38f 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -37,7 +37,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -48,6 +48,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -261,6 +266,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -285,6 +318,10 @@ pgstat_flush_backend(bool nowait, uint32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -300,6 +337,9 @@ pgstat_flush_backend(bool nowait, uint32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index 933dcb5cae5..218ff5e653c 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -352,6 +352,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 8e3549c3752..5d73c4dfbcf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -523,6 +523,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -535,6 +537,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -844,6 +852,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index eed4c6b359c..8077c65e938 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -707,7 +707,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, uint32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--uN5gGi2rtCNSVbpC
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v5-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v1] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 2 +
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 160 insertions(+), 1 deletion(-)
26.9% doc/src/sgml/
29.1% src/backend/utils/activity/
12.3% src/backend/utils/adt/
9.7% src/include/catalog/
4.0% src/include/utils/
9.2% src/test/regress/expected/
7.3% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index fa78031ccbb..ef89683164f 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..7cec0c83071 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,13 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static int pgStatBackendXactCommit = 0;
+static int pgStatBackendXactRollback = 0;
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +266,33 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += pgStatBackendXactCommit;
+ shbackendent->stats.xact_rollback += pgStatBackendXactRollback;
+
+ pgStatBackendXactCommit = pgStatBackendXactRollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +317,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +336,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +441,22 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ pgStatBackendXactCommit++;
+ else
+ pgStatBackendXactRollback++;
+
+ backend_has_xactstats = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..4c9f6b0dcec 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--ZnbSmJtA6EgjuGCe--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v2] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 166 insertions(+), 1 deletion(-)
26.1% doc/src/sgml/
29.1% src/backend/utils/activity/
11.9% src/backend/utils/adt/
9.4% src/include/catalog/
3.9% src/include/utils/
3.3% src/include/
8.9% src/test/regress/expected/
7.1% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 3f4a27a736e..77b41e02b82 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..12ae9a8d321 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,23 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..d3491faaff2 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Xact statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--SywurcztURg2uk+W--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v3 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. A new function is called in AtEOXact_PgStat() to increment those
two new counters.
---
src/backend/utils/activity/pgstat_backend.c | 57 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
4 files changed, 69 insertions(+), 1 deletion(-)
78.9% src/backend/utils/activity/
11.2% src/include/utils/
9.8% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..bf164854c4b 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,20 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /* Count transaction commit or abort */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..9efc0e10ebf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
--
2.34.1
--u6+Li7FU79Mt/lVd
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v3-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v4 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..47ce61e5093 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -36,7 +36,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index b31f20d41bc..400a8c5d734 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -342,6 +342,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..0fdbaf79780 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -801,6 +809,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..b97184c6539 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,7 +616,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--jg9kqX3osLGO+8A7
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v4-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v5 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 7727fed3bda..42bfb9cd38f 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -37,7 +37,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -48,6 +48,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -261,6 +266,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -285,6 +318,10 @@ pgstat_flush_backend(bool nowait, uint32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -300,6 +337,9 @@ pgstat_flush_backend(bool nowait, uint32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index 933dcb5cae5..218ff5e653c 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -352,6 +352,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 8e3549c3752..5d73c4dfbcf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -523,6 +523,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -535,6 +537,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -844,6 +852,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index eed4c6b359c..8077c65e938 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -707,7 +707,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, uint32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--uN5gGi2rtCNSVbpC
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v5-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v1] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 2 +
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 160 insertions(+), 1 deletion(-)
26.9% doc/src/sgml/
29.1% src/backend/utils/activity/
12.3% src/backend/utils/adt/
9.7% src/include/catalog/
4.0% src/include/utils/
9.2% src/test/regress/expected/
7.3% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index fa78031ccbb..ef89683164f 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..7cec0c83071 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,13 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static int pgStatBackendXactCommit = 0;
+static int pgStatBackendXactRollback = 0;
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +266,33 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += pgStatBackendXactCommit;
+ shbackendent->stats.xact_rollback += pgStatBackendXactRollback;
+
+ pgStatBackendXactCommit = pgStatBackendXactRollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +317,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +336,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +441,22 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ pgStatBackendXactCommit++;
+ else
+ pgStatBackendXactRollback++;
+
+ backend_has_xactstats = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..4c9f6b0dcec 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--ZnbSmJtA6EgjuGCe--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v2] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 166 insertions(+), 1 deletion(-)
26.1% doc/src/sgml/
29.1% src/backend/utils/activity/
11.9% src/backend/utils/adt/
9.4% src/include/catalog/
3.9% src/include/utils/
3.3% src/include/
8.9% src/test/regress/expected/
7.1% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 3f4a27a736e..77b41e02b82 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..12ae9a8d321 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,23 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..d3491faaff2 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Xact statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--SywurcztURg2uk+W--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v3 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. A new function is called in AtEOXact_PgStat() to increment those
two new counters.
---
src/backend/utils/activity/pgstat_backend.c | 57 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
4 files changed, 69 insertions(+), 1 deletion(-)
78.9% src/backend/utils/activity/
11.2% src/include/utils/
9.8% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..bf164854c4b 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,20 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /* Count transaction commit or abort */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..9efc0e10ebf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
--
2.34.1
--u6+Li7FU79Mt/lVd
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v3-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v4 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..47ce61e5093 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -36,7 +36,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index b31f20d41bc..400a8c5d734 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -342,6 +342,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..0fdbaf79780 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -801,6 +809,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..b97184c6539 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,7 +616,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--jg9kqX3osLGO+8A7
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v4-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v5 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 7727fed3bda..42bfb9cd38f 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -37,7 +37,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -48,6 +48,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -261,6 +266,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -285,6 +318,10 @@ pgstat_flush_backend(bool nowait, uint32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -300,6 +337,9 @@ pgstat_flush_backend(bool nowait, uint32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index 933dcb5cae5..218ff5e653c 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -352,6 +352,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 8e3549c3752..5d73c4dfbcf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -523,6 +523,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -535,6 +537,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -844,6 +852,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index eed4c6b359c..8077c65e938 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -707,7 +707,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, uint32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--uN5gGi2rtCNSVbpC
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v5-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v1] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 2 +
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 160 insertions(+), 1 deletion(-)
26.9% doc/src/sgml/
29.1% src/backend/utils/activity/
12.3% src/backend/utils/adt/
9.7% src/include/catalog/
4.0% src/include/utils/
9.2% src/test/regress/expected/
7.3% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index fa78031ccbb..ef89683164f 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..7cec0c83071 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,13 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static int pgStatBackendXactCommit = 0;
+static int pgStatBackendXactRollback = 0;
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +266,33 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += pgStatBackendXactCommit;
+ shbackendent->stats.xact_rollback += pgStatBackendXactRollback;
+
+ pgStatBackendXactCommit = pgStatBackendXactRollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +317,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +336,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +441,22 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ pgStatBackendXactCommit++;
+ else
+ pgStatBackendXactRollback++;
+
+ backend_has_xactstats = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..4c9f6b0dcec 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--ZnbSmJtA6EgjuGCe--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v2] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 166 insertions(+), 1 deletion(-)
26.1% doc/src/sgml/
29.1% src/backend/utils/activity/
11.9% src/backend/utils/adt/
9.4% src/include/catalog/
3.9% src/include/utils/
3.3% src/include/
8.9% src/test/regress/expected/
7.1% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 3f4a27a736e..77b41e02b82 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..12ae9a8d321 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,23 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..d3491faaff2 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Xact statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--SywurcztURg2uk+W--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v3 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. A new function is called in AtEOXact_PgStat() to increment those
two new counters.
---
src/backend/utils/activity/pgstat_backend.c | 57 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
4 files changed, 69 insertions(+), 1 deletion(-)
78.9% src/backend/utils/activity/
11.2% src/include/utils/
9.8% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..bf164854c4b 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,20 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /* Count transaction commit or abort */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..9efc0e10ebf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
--
2.34.1
--u6+Li7FU79Mt/lVd
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v3-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v4 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..47ce61e5093 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -36,7 +36,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index b31f20d41bc..400a8c5d734 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -342,6 +342,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..0fdbaf79780 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -801,6 +809,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..b97184c6539 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,7 +616,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--jg9kqX3osLGO+8A7
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v4-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v5 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 7727fed3bda..42bfb9cd38f 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -37,7 +37,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -48,6 +48,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -261,6 +266,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -285,6 +318,10 @@ pgstat_flush_backend(bool nowait, uint32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -300,6 +337,9 @@ pgstat_flush_backend(bool nowait, uint32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index 933dcb5cae5..218ff5e653c 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -352,6 +352,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 8e3549c3752..5d73c4dfbcf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -523,6 +523,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -535,6 +537,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -844,6 +852,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index eed4c6b359c..8077c65e938 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -707,7 +707,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, uint32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--uN5gGi2rtCNSVbpC
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v5-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v1] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 2 +
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 160 insertions(+), 1 deletion(-)
26.9% doc/src/sgml/
29.1% src/backend/utils/activity/
12.3% src/backend/utils/adt/
9.7% src/include/catalog/
4.0% src/include/utils/
9.2% src/test/regress/expected/
7.3% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index fa78031ccbb..ef89683164f 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..7cec0c83071 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,13 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static int pgStatBackendXactCommit = 0;
+static int pgStatBackendXactRollback = 0;
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +266,33 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += pgStatBackendXactCommit;
+ shbackendent->stats.xact_rollback += pgStatBackendXactRollback;
+
+ pgStatBackendXactCommit = pgStatBackendXactRollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +317,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +336,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +441,22 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ pgStatBackendXactCommit++;
+ else
+ pgStatBackendXactRollback++;
+
+ backend_has_xactstats = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..4c9f6b0dcec 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--ZnbSmJtA6EgjuGCe--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v2] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 166 insertions(+), 1 deletion(-)
26.1% doc/src/sgml/
29.1% src/backend/utils/activity/
11.9% src/backend/utils/adt/
9.4% src/include/catalog/
3.9% src/include/utils/
3.3% src/include/
8.9% src/test/regress/expected/
7.1% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 3f4a27a736e..77b41e02b82 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..12ae9a8d321 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,23 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..d3491faaff2 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Xact statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--SywurcztURg2uk+W--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v3 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. A new function is called in AtEOXact_PgStat() to increment those
two new counters.
---
src/backend/utils/activity/pgstat_backend.c | 57 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
4 files changed, 69 insertions(+), 1 deletion(-)
78.9% src/backend/utils/activity/
11.2% src/include/utils/
9.8% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..bf164854c4b 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,20 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /* Count transaction commit or abort */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..9efc0e10ebf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
--
2.34.1
--u6+Li7FU79Mt/lVd
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v3-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v4 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..47ce61e5093 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -36,7 +36,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index b31f20d41bc..400a8c5d734 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -342,6 +342,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..0fdbaf79780 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -801,6 +809,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..b97184c6539 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,7 +616,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--jg9kqX3osLGO+8A7
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v4-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v5 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 7727fed3bda..42bfb9cd38f 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -37,7 +37,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -48,6 +48,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -261,6 +266,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -285,6 +318,10 @@ pgstat_flush_backend(bool nowait, uint32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -300,6 +337,9 @@ pgstat_flush_backend(bool nowait, uint32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index 933dcb5cae5..218ff5e653c 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -352,6 +352,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 8e3549c3752..5d73c4dfbcf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -523,6 +523,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -535,6 +537,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -844,6 +852,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index eed4c6b359c..8077c65e938 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -707,7 +707,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, uint32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--uN5gGi2rtCNSVbpC
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v5-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v1] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 2 +
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 160 insertions(+), 1 deletion(-)
26.9% doc/src/sgml/
29.1% src/backend/utils/activity/
12.3% src/backend/utils/adt/
9.7% src/include/catalog/
4.0% src/include/utils/
9.2% src/test/regress/expected/
7.3% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index fa78031ccbb..ef89683164f 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..7cec0c83071 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,13 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static int pgStatBackendXactCommit = 0;
+static int pgStatBackendXactRollback = 0;
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +266,33 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += pgStatBackendXactCommit;
+ shbackendent->stats.xact_rollback += pgStatBackendXactRollback;
+
+ pgStatBackendXactCommit = pgStatBackendXactRollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +317,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +336,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +441,22 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ pgStatBackendXactCommit++;
+ else
+ pgStatBackendXactRollback++;
+
+ backend_has_xactstats = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..4c9f6b0dcec 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--ZnbSmJtA6EgjuGCe--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v2] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 166 insertions(+), 1 deletion(-)
26.1% doc/src/sgml/
29.1% src/backend/utils/activity/
11.9% src/backend/utils/adt/
9.4% src/include/catalog/
3.9% src/include/utils/
3.3% src/include/
8.9% src/test/regress/expected/
7.1% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 3f4a27a736e..77b41e02b82 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..12ae9a8d321 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,23 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..d3491faaff2 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Xact statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--SywurcztURg2uk+W--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v3 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. A new function is called in AtEOXact_PgStat() to increment those
two new counters.
---
src/backend/utils/activity/pgstat_backend.c | 57 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
4 files changed, 69 insertions(+), 1 deletion(-)
78.9% src/backend/utils/activity/
11.2% src/include/utils/
9.8% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..bf164854c4b 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,20 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /* Count transaction commit or abort */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..9efc0e10ebf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
--
2.34.1
--u6+Li7FU79Mt/lVd
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v3-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v4 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..47ce61e5093 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -36,7 +36,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index b31f20d41bc..400a8c5d734 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -342,6 +342,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..0fdbaf79780 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -801,6 +809,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..b97184c6539 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,7 +616,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--jg9kqX3osLGO+8A7
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v4-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v5 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 7727fed3bda..42bfb9cd38f 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -37,7 +37,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -48,6 +48,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -261,6 +266,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -285,6 +318,10 @@ pgstat_flush_backend(bool nowait, uint32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -300,6 +337,9 @@ pgstat_flush_backend(bool nowait, uint32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index 933dcb5cae5..218ff5e653c 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -352,6 +352,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 8e3549c3752..5d73c4dfbcf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -523,6 +523,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -535,6 +537,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -844,6 +852,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index eed4c6b359c..8077c65e938 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -707,7 +707,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, uint32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--uN5gGi2rtCNSVbpC
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v5-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v1] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 2 +
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 160 insertions(+), 1 deletion(-)
26.9% doc/src/sgml/
29.1% src/backend/utils/activity/
12.3% src/backend/utils/adt/
9.7% src/include/catalog/
4.0% src/include/utils/
9.2% src/test/regress/expected/
7.3% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index fa78031ccbb..ef89683164f 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..7cec0c83071 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,13 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static int pgStatBackendXactCommit = 0;
+static int pgStatBackendXactRollback = 0;
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +266,33 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += pgStatBackendXactCommit;
+ shbackendent->stats.xact_rollback += pgStatBackendXactRollback;
+
+ pgStatBackendXactCommit = pgStatBackendXactRollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +317,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +336,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +441,22 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ pgStatBackendXactCommit++;
+ else
+ pgStatBackendXactRollback++;
+
+ backend_has_xactstats = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..4c9f6b0dcec 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--ZnbSmJtA6EgjuGCe--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v2] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 166 insertions(+), 1 deletion(-)
26.1% doc/src/sgml/
29.1% src/backend/utils/activity/
11.9% src/backend/utils/adt/
9.4% src/include/catalog/
3.9% src/include/utils/
3.3% src/include/
8.9% src/test/regress/expected/
7.1% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 3f4a27a736e..77b41e02b82 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..12ae9a8d321 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,23 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..d3491faaff2 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Xact statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--SywurcztURg2uk+W--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v3 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. A new function is called in AtEOXact_PgStat() to increment those
two new counters.
---
src/backend/utils/activity/pgstat_backend.c | 57 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
4 files changed, 69 insertions(+), 1 deletion(-)
78.9% src/backend/utils/activity/
11.2% src/include/utils/
9.8% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..bf164854c4b 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,20 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /* Count transaction commit or abort */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..9efc0e10ebf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
--
2.34.1
--u6+Li7FU79Mt/lVd
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v3-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v4 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..47ce61e5093 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -36,7 +36,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index b31f20d41bc..400a8c5d734 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -342,6 +342,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..0fdbaf79780 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -801,6 +809,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..b97184c6539 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,7 +616,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--jg9kqX3osLGO+8A7
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v4-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v5 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 7727fed3bda..42bfb9cd38f 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -37,7 +37,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -48,6 +48,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -261,6 +266,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -285,6 +318,10 @@ pgstat_flush_backend(bool nowait, uint32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -300,6 +337,9 @@ pgstat_flush_backend(bool nowait, uint32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index 933dcb5cae5..218ff5e653c 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -352,6 +352,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 8e3549c3752..5d73c4dfbcf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -523,6 +523,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -535,6 +537,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -844,6 +852,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index eed4c6b359c..8077c65e938 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -707,7 +707,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, uint32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--uN5gGi2rtCNSVbpC
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v5-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v4 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..47ce61e5093 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -36,7 +36,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index b31f20d41bc..400a8c5d734 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -342,6 +342,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..0fdbaf79780 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -801,6 +809,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..b97184c6539 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,7 +616,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--jg9kqX3osLGO+8A7
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v4-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v1] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 2 +
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 160 insertions(+), 1 deletion(-)
26.9% doc/src/sgml/
29.1% src/backend/utils/activity/
12.3% src/backend/utils/adt/
9.7% src/include/catalog/
4.0% src/include/utils/
9.2% src/test/regress/expected/
7.3% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index fa78031ccbb..ef89683164f 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..7cec0c83071 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,13 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static int pgStatBackendXactCommit = 0;
+static int pgStatBackendXactRollback = 0;
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +266,33 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += pgStatBackendXactCommit;
+ shbackendent->stats.xact_rollback += pgStatBackendXactRollback;
+
+ pgStatBackendXactCommit = pgStatBackendXactRollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +317,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +336,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +441,22 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ pgStatBackendXactCommit++;
+ else
+ pgStatBackendXactRollback++;
+
+ backend_has_xactstats = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..4c9f6b0dcec 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--ZnbSmJtA6EgjuGCe--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v2] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 166 insertions(+), 1 deletion(-)
26.1% doc/src/sgml/
29.1% src/backend/utils/activity/
11.9% src/backend/utils/adt/
9.4% src/include/catalog/
3.9% src/include/utils/
3.3% src/include/
8.9% src/test/regress/expected/
7.1% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 3f4a27a736e..77b41e02b82 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..12ae9a8d321 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,23 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..d3491faaff2 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Xact statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--SywurcztURg2uk+W--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v3 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. A new function is called in AtEOXact_PgStat() to increment those
two new counters.
---
src/backend/utils/activity/pgstat_backend.c | 57 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
4 files changed, 69 insertions(+), 1 deletion(-)
78.9% src/backend/utils/activity/
11.2% src/include/utils/
9.8% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..bf164854c4b 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,20 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /* Count transaction commit or abort */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..9efc0e10ebf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
--
2.34.1
--u6+Li7FU79Mt/lVd
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v3-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v5 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 7727fed3bda..42bfb9cd38f 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -37,7 +37,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -48,6 +48,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -261,6 +266,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -285,6 +318,10 @@ pgstat_flush_backend(bool nowait, uint32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -300,6 +337,9 @@ pgstat_flush_backend(bool nowait, uint32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index 933dcb5cae5..218ff5e653c 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -352,6 +352,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 8e3549c3752..5d73c4dfbcf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -523,6 +523,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -535,6 +537,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -844,6 +852,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index eed4c6b359c..8077c65e938 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -707,7 +707,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, uint32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--uN5gGi2rtCNSVbpC
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v5-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v1] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 2 +
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 160 insertions(+), 1 deletion(-)
26.9% doc/src/sgml/
29.1% src/backend/utils/activity/
12.3% src/backend/utils/adt/
9.7% src/include/catalog/
4.0% src/include/utils/
9.2% src/test/regress/expected/
7.3% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index fa78031ccbb..ef89683164f 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..7cec0c83071 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,13 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static int pgStatBackendXactCommit = 0;
+static int pgStatBackendXactRollback = 0;
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +266,33 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += pgStatBackendXactCommit;
+ shbackendent->stats.xact_rollback += pgStatBackendXactRollback;
+
+ pgStatBackendXactCommit = pgStatBackendXactRollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +317,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +336,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +441,22 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ pgStatBackendXactCommit++;
+ else
+ pgStatBackendXactRollback++;
+
+ backend_has_xactstats = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..4c9f6b0dcec 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--ZnbSmJtA6EgjuGCe--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v2] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 166 insertions(+), 1 deletion(-)
26.1% doc/src/sgml/
29.1% src/backend/utils/activity/
11.9% src/backend/utils/adt/
9.4% src/include/catalog/
3.9% src/include/utils/
3.3% src/include/
8.9% src/test/regress/expected/
7.1% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 3f4a27a736e..77b41e02b82 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..12ae9a8d321 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,23 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..d3491faaff2 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Xact statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--SywurcztURg2uk+W--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v4 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..47ce61e5093 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -36,7 +36,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index b31f20d41bc..400a8c5d734 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -342,6 +342,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..0fdbaf79780 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -801,6 +809,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..b97184c6539 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,7 +616,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--jg9kqX3osLGO+8A7
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v4-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v5 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 7727fed3bda..42bfb9cd38f 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -37,7 +37,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -48,6 +48,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -261,6 +266,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -285,6 +318,10 @@ pgstat_flush_backend(bool nowait, uint32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -300,6 +337,9 @@ pgstat_flush_backend(bool nowait, uint32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index 933dcb5cae5..218ff5e653c 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -352,6 +352,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 8e3549c3752..5d73c4dfbcf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -523,6 +523,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -535,6 +537,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -844,6 +852,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index eed4c6b359c..8077c65e938 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -707,7 +707,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, uint32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--uN5gGi2rtCNSVbpC
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v5-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v1] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 2 +
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 160 insertions(+), 1 deletion(-)
26.9% doc/src/sgml/
29.1% src/backend/utils/activity/
12.3% src/backend/utils/adt/
9.7% src/include/catalog/
4.0% src/include/utils/
9.2% src/test/regress/expected/
7.3% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index fa78031ccbb..ef89683164f 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..7cec0c83071 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,13 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static int pgStatBackendXactCommit = 0;
+static int pgStatBackendXactRollback = 0;
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +266,33 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += pgStatBackendXactCommit;
+ shbackendent->stats.xact_rollback += pgStatBackendXactRollback;
+
+ pgStatBackendXactCommit = pgStatBackendXactRollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +317,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +336,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +441,22 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ pgStatBackendXactCommit++;
+ else
+ pgStatBackendXactRollback++;
+
+ backend_has_xactstats = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..4c9f6b0dcec 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--ZnbSmJtA6EgjuGCe--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v2] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 166 insertions(+), 1 deletion(-)
26.1% doc/src/sgml/
29.1% src/backend/utils/activity/
11.9% src/backend/utils/adt/
9.4% src/include/catalog/
3.9% src/include/utils/
3.3% src/include/
8.9% src/test/regress/expected/
7.1% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 3f4a27a736e..77b41e02b82 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..12ae9a8d321 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,23 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..d3491faaff2 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Xact statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--SywurcztURg2uk+W--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v3 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. A new function is called in AtEOXact_PgStat() to increment those
two new counters.
---
src/backend/utils/activity/pgstat_backend.c | 57 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
4 files changed, 69 insertions(+), 1 deletion(-)
78.9% src/backend/utils/activity/
11.2% src/include/utils/
9.8% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..bf164854c4b 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,20 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /* Count transaction commit or abort */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..9efc0e10ebf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
--
2.34.1
--u6+Li7FU79Mt/lVd
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v3-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v4 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..47ce61e5093 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -36,7 +36,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index b31f20d41bc..400a8c5d734 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -342,6 +342,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..0fdbaf79780 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -801,6 +809,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..b97184c6539 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,7 +616,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--jg9kqX3osLGO+8A7
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v4-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v5 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 7727fed3bda..42bfb9cd38f 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -37,7 +37,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -48,6 +48,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -261,6 +266,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -285,6 +318,10 @@ pgstat_flush_backend(bool nowait, uint32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -300,6 +337,9 @@ pgstat_flush_backend(bool nowait, uint32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index 933dcb5cae5..218ff5e653c 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -352,6 +352,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 8e3549c3752..5d73c4dfbcf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -523,6 +523,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -535,6 +537,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -844,6 +852,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index eed4c6b359c..8077c65e938 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -707,7 +707,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, uint32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--uN5gGi2rtCNSVbpC
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v5-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v1] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 2 +
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 160 insertions(+), 1 deletion(-)
26.9% doc/src/sgml/
29.1% src/backend/utils/activity/
12.3% src/backend/utils/adt/
9.7% src/include/catalog/
4.0% src/include/utils/
9.2% src/test/regress/expected/
7.3% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index fa78031ccbb..ef89683164f 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..7cec0c83071 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,13 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static int pgStatBackendXactCommit = 0;
+static int pgStatBackendXactRollback = 0;
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +266,33 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += pgStatBackendXactCommit;
+ shbackendent->stats.xact_rollback += pgStatBackendXactRollback;
+
+ pgStatBackendXactCommit = pgStatBackendXactRollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +317,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +336,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +441,22 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ pgStatBackendXactCommit++;
+ else
+ pgStatBackendXactRollback++;
+
+ backend_has_xactstats = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..4c9f6b0dcec 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--ZnbSmJtA6EgjuGCe--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v2] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 166 insertions(+), 1 deletion(-)
26.1% doc/src/sgml/
29.1% src/backend/utils/activity/
11.9% src/backend/utils/adt/
9.4% src/include/catalog/
3.9% src/include/utils/
3.3% src/include/
8.9% src/test/regress/expected/
7.1% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 3f4a27a736e..77b41e02b82 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..12ae9a8d321 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,23 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..d3491faaff2 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Xact statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--SywurcztURg2uk+W--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v3 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. A new function is called in AtEOXact_PgStat() to increment those
two new counters.
---
src/backend/utils/activity/pgstat_backend.c | 57 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
4 files changed, 69 insertions(+), 1 deletion(-)
78.9% src/backend/utils/activity/
11.2% src/include/utils/
9.8% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..bf164854c4b 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,20 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /* Count transaction commit or abort */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..9efc0e10ebf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
--
2.34.1
--u6+Li7FU79Mt/lVd
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v3-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v4 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..47ce61e5093 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -36,7 +36,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index b31f20d41bc..400a8c5d734 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -342,6 +342,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..0fdbaf79780 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -801,6 +809,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..b97184c6539 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,7 +616,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--jg9kqX3osLGO+8A7
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v4-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v1] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 2 +
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 160 insertions(+), 1 deletion(-)
26.9% doc/src/sgml/
29.1% src/backend/utils/activity/
12.3% src/backend/utils/adt/
9.7% src/include/catalog/
4.0% src/include/utils/
9.2% src/test/regress/expected/
7.3% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index fa78031ccbb..ef89683164f 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..7cec0c83071 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,13 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static int pgStatBackendXactCommit = 0;
+static int pgStatBackendXactRollback = 0;
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +266,33 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += pgStatBackendXactCommit;
+ shbackendent->stats.xact_rollback += pgStatBackendXactRollback;
+
+ pgStatBackendXactCommit = pgStatBackendXactRollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +317,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +336,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +441,22 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ pgStatBackendXactCommit++;
+ else
+ pgStatBackendXactRollback++;
+
+ backend_has_xactstats = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..4c9f6b0dcec 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--ZnbSmJtA6EgjuGCe--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v2] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 166 insertions(+), 1 deletion(-)
26.1% doc/src/sgml/
29.1% src/backend/utils/activity/
11.9% src/backend/utils/adt/
9.4% src/include/catalog/
3.9% src/include/utils/
3.3% src/include/
8.9% src/test/regress/expected/
7.1% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 3f4a27a736e..77b41e02b82 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..12ae9a8d321 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,23 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..d3491faaff2 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Xact statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--SywurcztURg2uk+W--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v3 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. A new function is called in AtEOXact_PgStat() to increment those
two new counters.
---
src/backend/utils/activity/pgstat_backend.c | 57 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
4 files changed, 69 insertions(+), 1 deletion(-)
78.9% src/backend/utils/activity/
11.2% src/include/utils/
9.8% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..bf164854c4b 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,20 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /* Count transaction commit or abort */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..9efc0e10ebf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
--
2.34.1
--u6+Li7FU79Mt/lVd
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v3-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v4 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..47ce61e5093 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -36,7 +36,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index b31f20d41bc..400a8c5d734 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -342,6 +342,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..0fdbaf79780 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -801,6 +809,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..b97184c6539 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,7 +616,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--jg9kqX3osLGO+8A7
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v4-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v5 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 7727fed3bda..42bfb9cd38f 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -37,7 +37,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -48,6 +48,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -261,6 +266,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -285,6 +318,10 @@ pgstat_flush_backend(bool nowait, uint32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -300,6 +337,9 @@ pgstat_flush_backend(bool nowait, uint32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index 933dcb5cae5..218ff5e653c 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -352,6 +352,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 8e3549c3752..5d73c4dfbcf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -523,6 +523,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -535,6 +537,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -844,6 +852,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index eed4c6b359c..8077c65e938 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -707,7 +707,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, uint32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--uN5gGi2rtCNSVbpC
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v5-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v1] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 2 +
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 160 insertions(+), 1 deletion(-)
26.9% doc/src/sgml/
29.1% src/backend/utils/activity/
12.3% src/backend/utils/adt/
9.7% src/include/catalog/
4.0% src/include/utils/
9.2% src/test/regress/expected/
7.3% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index fa78031ccbb..ef89683164f 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..7cec0c83071 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,13 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static int pgStatBackendXactCommit = 0;
+static int pgStatBackendXactRollback = 0;
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +266,33 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += pgStatBackendXactCommit;
+ shbackendent->stats.xact_rollback += pgStatBackendXactRollback;
+
+ pgStatBackendXactCommit = pgStatBackendXactRollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +317,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +336,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +441,22 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ pgStatBackendXactCommit++;
+ else
+ pgStatBackendXactRollback++;
+
+ backend_has_xactstats = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..4c9f6b0dcec 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--ZnbSmJtA6EgjuGCe--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v2] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 166 insertions(+), 1 deletion(-)
26.1% doc/src/sgml/
29.1% src/backend/utils/activity/
11.9% src/backend/utils/adt/
9.4% src/include/catalog/
3.9% src/include/utils/
3.3% src/include/
8.9% src/test/regress/expected/
7.1% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 3f4a27a736e..77b41e02b82 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..12ae9a8d321 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,23 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..d3491faaff2 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Xact statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--SywurcztURg2uk+W--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v3 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. A new function is called in AtEOXact_PgStat() to increment those
two new counters.
---
src/backend/utils/activity/pgstat_backend.c | 57 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
4 files changed, 69 insertions(+), 1 deletion(-)
78.9% src/backend/utils/activity/
11.2% src/include/utils/
9.8% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..bf164854c4b 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,20 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /* Count transaction commit or abort */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..9efc0e10ebf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
--
2.34.1
--u6+Li7FU79Mt/lVd
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v3-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v4 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..47ce61e5093 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -36,7 +36,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index b31f20d41bc..400a8c5d734 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -342,6 +342,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..0fdbaf79780 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -801,6 +809,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..b97184c6539 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,7 +616,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--jg9kqX3osLGO+8A7
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v4-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v5 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 7727fed3bda..42bfb9cd38f 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -37,7 +37,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -48,6 +48,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -261,6 +266,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -285,6 +318,10 @@ pgstat_flush_backend(bool nowait, uint32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -300,6 +337,9 @@ pgstat_flush_backend(bool nowait, uint32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index 933dcb5cae5..218ff5e653c 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -352,6 +352,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 8e3549c3752..5d73c4dfbcf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -523,6 +523,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -535,6 +537,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -844,6 +852,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index eed4c6b359c..8077c65e938 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -707,7 +707,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, uint32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--uN5gGi2rtCNSVbpC
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v5-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v1] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 2 +
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 160 insertions(+), 1 deletion(-)
26.9% doc/src/sgml/
29.1% src/backend/utils/activity/
12.3% src/backend/utils/adt/
9.7% src/include/catalog/
4.0% src/include/utils/
9.2% src/test/regress/expected/
7.3% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index fa78031ccbb..ef89683164f 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..7cec0c83071 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,13 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static int pgStatBackendXactCommit = 0;
+static int pgStatBackendXactRollback = 0;
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +266,33 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += pgStatBackendXactCommit;
+ shbackendent->stats.xact_rollback += pgStatBackendXactRollback;
+
+ pgStatBackendXactCommit = pgStatBackendXactRollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +317,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +336,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +441,22 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ pgStatBackendXactCommit++;
+ else
+ pgStatBackendXactRollback++;
+
+ backend_has_xactstats = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..4c9f6b0dcec 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--ZnbSmJtA6EgjuGCe--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v2] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 166 insertions(+), 1 deletion(-)
26.1% doc/src/sgml/
29.1% src/backend/utils/activity/
11.9% src/backend/utils/adt/
9.4% src/include/catalog/
3.9% src/include/utils/
3.3% src/include/
8.9% src/test/regress/expected/
7.1% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 3f4a27a736e..77b41e02b82 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..12ae9a8d321 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,23 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..d3491faaff2 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Xact statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--SywurcztURg2uk+W--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v3 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. A new function is called in AtEOXact_PgStat() to increment those
two new counters.
---
src/backend/utils/activity/pgstat_backend.c | 57 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
4 files changed, 69 insertions(+), 1 deletion(-)
78.9% src/backend/utils/activity/
11.2% src/include/utils/
9.8% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..bf164854c4b 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,20 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /* Count transaction commit or abort */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..9efc0e10ebf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
--
2.34.1
--u6+Li7FU79Mt/lVd
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v3-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v1] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 2 +
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 160 insertions(+), 1 deletion(-)
26.9% doc/src/sgml/
29.1% src/backend/utils/activity/
12.3% src/backend/utils/adt/
9.7% src/include/catalog/
4.0% src/include/utils/
9.2% src/test/regress/expected/
7.3% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index fa78031ccbb..ef89683164f 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..7cec0c83071 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,13 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static int pgStatBackendXactCommit = 0;
+static int pgStatBackendXactRollback = 0;
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +266,33 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += pgStatBackendXactCommit;
+ shbackendent->stats.xact_rollback += pgStatBackendXactRollback;
+
+ pgStatBackendXactCommit = pgStatBackendXactRollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +317,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +336,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +441,22 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ pgStatBackendXactCommit++;
+ else
+ pgStatBackendXactRollback++;
+
+ backend_has_xactstats = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..4c9f6b0dcec 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--ZnbSmJtA6EgjuGCe--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v2] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 166 insertions(+), 1 deletion(-)
26.1% doc/src/sgml/
29.1% src/backend/utils/activity/
11.9% src/backend/utils/adt/
9.4% src/include/catalog/
3.9% src/include/utils/
3.3% src/include/
8.9% src/test/regress/expected/
7.1% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 3f4a27a736e..77b41e02b82 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..12ae9a8d321 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,23 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..d3491faaff2 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Xact statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--SywurcztURg2uk+W--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v3 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. A new function is called in AtEOXact_PgStat() to increment those
two new counters.
---
src/backend/utils/activity/pgstat_backend.c | 57 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
4 files changed, 69 insertions(+), 1 deletion(-)
78.9% src/backend/utils/activity/
11.2% src/include/utils/
9.8% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..bf164854c4b 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,20 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /* Count transaction commit or abort */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..9efc0e10ebf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
--
2.34.1
--u6+Li7FU79Mt/lVd
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v3-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v4 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..47ce61e5093 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -36,7 +36,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index b31f20d41bc..400a8c5d734 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -342,6 +342,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..0fdbaf79780 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -801,6 +809,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..b97184c6539 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,7 +616,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--jg9kqX3osLGO+8A7
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v4-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v5 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 7727fed3bda..42bfb9cd38f 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -37,7 +37,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -48,6 +48,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -261,6 +266,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -285,6 +318,10 @@ pgstat_flush_backend(bool nowait, uint32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -300,6 +337,9 @@ pgstat_flush_backend(bool nowait, uint32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index 933dcb5cae5..218ff5e653c 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -352,6 +352,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 8e3549c3752..5d73c4dfbcf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -523,6 +523,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -535,6 +537,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -844,6 +852,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index eed4c6b359c..8077c65e938 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -707,7 +707,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, uint32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--uN5gGi2rtCNSVbpC
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v5-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v1] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 2 +
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 160 insertions(+), 1 deletion(-)
26.9% doc/src/sgml/
29.1% src/backend/utils/activity/
12.3% src/backend/utils/adt/
9.7% src/include/catalog/
4.0% src/include/utils/
9.2% src/test/regress/expected/
7.3% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index fa78031ccbb..ef89683164f 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..7cec0c83071 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,13 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static int pgStatBackendXactCommit = 0;
+static int pgStatBackendXactRollback = 0;
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +266,33 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += pgStatBackendXactCommit;
+ shbackendent->stats.xact_rollback += pgStatBackendXactRollback;
+
+ pgStatBackendXactCommit = pgStatBackendXactRollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +317,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +336,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +441,22 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ pgStatBackendXactCommit++;
+ else
+ pgStatBackendXactRollback++;
+
+ backend_has_xactstats = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..4c9f6b0dcec 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--ZnbSmJtA6EgjuGCe--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v2] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 166 insertions(+), 1 deletion(-)
26.1% doc/src/sgml/
29.1% src/backend/utils/activity/
11.9% src/backend/utils/adt/
9.4% src/include/catalog/
3.9% src/include/utils/
3.3% src/include/
8.9% src/test/regress/expected/
7.1% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 3f4a27a736e..77b41e02b82 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..12ae9a8d321 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,23 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..d3491faaff2 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Xact statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--SywurcztURg2uk+W--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v3 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. A new function is called in AtEOXact_PgStat() to increment those
two new counters.
---
src/backend/utils/activity/pgstat_backend.c | 57 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
4 files changed, 69 insertions(+), 1 deletion(-)
78.9% src/backend/utils/activity/
11.2% src/include/utils/
9.8% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..bf164854c4b 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,20 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /* Count transaction commit or abort */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..9efc0e10ebf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
--
2.34.1
--u6+Li7FU79Mt/lVd
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v3-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v4 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..47ce61e5093 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -36,7 +36,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index b31f20d41bc..400a8c5d734 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -342,6 +342,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..0fdbaf79780 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -801,6 +809,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..b97184c6539 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,7 +616,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--jg9kqX3osLGO+8A7
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v4-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v5 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 7727fed3bda..42bfb9cd38f 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -37,7 +37,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -48,6 +48,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -261,6 +266,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -285,6 +318,10 @@ pgstat_flush_backend(bool nowait, uint32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -300,6 +337,9 @@ pgstat_flush_backend(bool nowait, uint32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index 933dcb5cae5..218ff5e653c 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -352,6 +352,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 8e3549c3752..5d73c4dfbcf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -523,6 +523,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -535,6 +537,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -844,6 +852,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index eed4c6b359c..8077c65e938 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -707,7 +707,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, uint32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--uN5gGi2rtCNSVbpC
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v5-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v1] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 2 +
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 160 insertions(+), 1 deletion(-)
26.9% doc/src/sgml/
29.1% src/backend/utils/activity/
12.3% src/backend/utils/adt/
9.7% src/include/catalog/
4.0% src/include/utils/
9.2% src/test/regress/expected/
7.3% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index fa78031ccbb..ef89683164f 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..7cec0c83071 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,13 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static int pgStatBackendXactCommit = 0;
+static int pgStatBackendXactRollback = 0;
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +266,33 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += pgStatBackendXactCommit;
+ shbackendent->stats.xact_rollback += pgStatBackendXactRollback;
+
+ pgStatBackendXactCommit = pgStatBackendXactRollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +317,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +336,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +441,22 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ pgStatBackendXactCommit++;
+ else
+ pgStatBackendXactRollback++;
+
+ backend_has_xactstats = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..4c9f6b0dcec 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--ZnbSmJtA6EgjuGCe--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v2] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 166 insertions(+), 1 deletion(-)
26.1% doc/src/sgml/
29.1% src/backend/utils/activity/
11.9% src/backend/utils/adt/
9.4% src/include/catalog/
3.9% src/include/utils/
3.3% src/include/
8.9% src/test/regress/expected/
7.1% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 3f4a27a736e..77b41e02b82 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..12ae9a8d321 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,23 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..d3491faaff2 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Xact statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--SywurcztURg2uk+W--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v3 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. A new function is called in AtEOXact_PgStat() to increment those
two new counters.
---
src/backend/utils/activity/pgstat_backend.c | 57 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
4 files changed, 69 insertions(+), 1 deletion(-)
78.9% src/backend/utils/activity/
11.2% src/include/utils/
9.8% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..bf164854c4b 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,20 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /* Count transaction commit or abort */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..9efc0e10ebf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
--
2.34.1
--u6+Li7FU79Mt/lVd
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v3-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v4 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..47ce61e5093 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -36,7 +36,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index b31f20d41bc..400a8c5d734 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -342,6 +342,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..0fdbaf79780 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -801,6 +809,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..b97184c6539 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,7 +616,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--jg9kqX3osLGO+8A7
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v4-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v5 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 7727fed3bda..42bfb9cd38f 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -37,7 +37,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -48,6 +48,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -261,6 +266,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -285,6 +318,10 @@ pgstat_flush_backend(bool nowait, uint32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -300,6 +337,9 @@ pgstat_flush_backend(bool nowait, uint32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index 933dcb5cae5..218ff5e653c 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -352,6 +352,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 8e3549c3752..5d73c4dfbcf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -523,6 +523,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -535,6 +537,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -844,6 +852,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index eed4c6b359c..8077c65e938 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -707,7 +707,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, uint32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--uN5gGi2rtCNSVbpC
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v5-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v1] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 2 +
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 160 insertions(+), 1 deletion(-)
26.9% doc/src/sgml/
29.1% src/backend/utils/activity/
12.3% src/backend/utils/adt/
9.7% src/include/catalog/
4.0% src/include/utils/
9.2% src/test/regress/expected/
7.3% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index fa78031ccbb..ef89683164f 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..7cec0c83071 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,13 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static int pgStatBackendXactCommit = 0;
+static int pgStatBackendXactRollback = 0;
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +266,33 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += pgStatBackendXactCommit;
+ shbackendent->stats.xact_rollback += pgStatBackendXactRollback;
+
+ pgStatBackendXactCommit = pgStatBackendXactRollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +317,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +336,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +441,22 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ pgStatBackendXactCommit++;
+ else
+ pgStatBackendXactRollback++;
+
+ backend_has_xactstats = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..4c9f6b0dcec 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--ZnbSmJtA6EgjuGCe--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v2] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 166 insertions(+), 1 deletion(-)
26.1% doc/src/sgml/
29.1% src/backend/utils/activity/
11.9% src/backend/utils/adt/
9.4% src/include/catalog/
3.9% src/include/utils/
3.3% src/include/
8.9% src/test/regress/expected/
7.1% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 3f4a27a736e..77b41e02b82 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..12ae9a8d321 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,23 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..d3491faaff2 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Xact statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--SywurcztURg2uk+W--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v3 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. A new function is called in AtEOXact_PgStat() to increment those
two new counters.
---
src/backend/utils/activity/pgstat_backend.c | 57 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
4 files changed, 69 insertions(+), 1 deletion(-)
78.9% src/backend/utils/activity/
11.2% src/include/utils/
9.8% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..bf164854c4b 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,20 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /* Count transaction commit or abort */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..9efc0e10ebf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
--
2.34.1
--u6+Li7FU79Mt/lVd
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v3-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v4 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..47ce61e5093 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -36,7 +36,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index b31f20d41bc..400a8c5d734 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -342,6 +342,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..0fdbaf79780 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -801,6 +809,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..b97184c6539 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,7 +616,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--jg9kqX3osLGO+8A7
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v4-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v5 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 7727fed3bda..42bfb9cd38f 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -37,7 +37,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -48,6 +48,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -261,6 +266,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -285,6 +318,10 @@ pgstat_flush_backend(bool nowait, uint32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -300,6 +337,9 @@ pgstat_flush_backend(bool nowait, uint32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index 933dcb5cae5..218ff5e653c 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -352,6 +352,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 8e3549c3752..5d73c4dfbcf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -523,6 +523,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -535,6 +537,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -844,6 +852,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index eed4c6b359c..8077c65e938 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -707,7 +707,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, uint32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--uN5gGi2rtCNSVbpC
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v5-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v1] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 2 +
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 160 insertions(+), 1 deletion(-)
26.9% doc/src/sgml/
29.1% src/backend/utils/activity/
12.3% src/backend/utils/adt/
9.7% src/include/catalog/
4.0% src/include/utils/
9.2% src/test/regress/expected/
7.3% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index fa78031ccbb..ef89683164f 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..7cec0c83071 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,13 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static int pgStatBackendXactCommit = 0;
+static int pgStatBackendXactRollback = 0;
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +266,33 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += pgStatBackendXactCommit;
+ shbackendent->stats.xact_rollback += pgStatBackendXactRollback;
+
+ pgStatBackendXactCommit = pgStatBackendXactRollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +317,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +336,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +441,22 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ pgStatBackendXactCommit++;
+ else
+ pgStatBackendXactRollback++;
+
+ backend_has_xactstats = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..4c9f6b0dcec 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--ZnbSmJtA6EgjuGCe--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v2] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 166 insertions(+), 1 deletion(-)
26.1% doc/src/sgml/
29.1% src/backend/utils/activity/
11.9% src/backend/utils/adt/
9.4% src/include/catalog/
3.9% src/include/utils/
3.3% src/include/
8.9% src/test/regress/expected/
7.1% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 3f4a27a736e..77b41e02b82 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..12ae9a8d321 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,23 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..d3491faaff2 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Xact statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--SywurcztURg2uk+W--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v3 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. A new function is called in AtEOXact_PgStat() to increment those
two new counters.
---
src/backend/utils/activity/pgstat_backend.c | 57 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
4 files changed, 69 insertions(+), 1 deletion(-)
78.9% src/backend/utils/activity/
11.2% src/include/utils/
9.8% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..bf164854c4b 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,20 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /* Count transaction commit or abort */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..9efc0e10ebf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
--
2.34.1
--u6+Li7FU79Mt/lVd
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v3-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v4 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..47ce61e5093 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -36,7 +36,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index b31f20d41bc..400a8c5d734 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -342,6 +342,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..0fdbaf79780 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -801,6 +809,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..b97184c6539 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,7 +616,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--jg9kqX3osLGO+8A7
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v4-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v5 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 7727fed3bda..42bfb9cd38f 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -37,7 +37,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -48,6 +48,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -261,6 +266,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -285,6 +318,10 @@ pgstat_flush_backend(bool nowait, uint32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -300,6 +337,9 @@ pgstat_flush_backend(bool nowait, uint32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index 933dcb5cae5..218ff5e653c 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -352,6 +352,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 8e3549c3752..5d73c4dfbcf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -523,6 +523,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -535,6 +537,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -844,6 +852,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index eed4c6b359c..8077c65e938 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -707,7 +707,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, uint32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--uN5gGi2rtCNSVbpC
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v5-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v1] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 2 +
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 160 insertions(+), 1 deletion(-)
26.9% doc/src/sgml/
29.1% src/backend/utils/activity/
12.3% src/backend/utils/adt/
9.7% src/include/catalog/
4.0% src/include/utils/
9.2% src/test/regress/expected/
7.3% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index fa78031ccbb..ef89683164f 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..7cec0c83071 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,13 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static int pgStatBackendXactCommit = 0;
+static int pgStatBackendXactRollback = 0;
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +266,33 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += pgStatBackendXactCommit;
+ shbackendent->stats.xact_rollback += pgStatBackendXactRollback;
+
+ pgStatBackendXactCommit = pgStatBackendXactRollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +317,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +336,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +441,22 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ pgStatBackendXactCommit++;
+ else
+ pgStatBackendXactRollback++;
+
+ backend_has_xactstats = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..4c9f6b0dcec 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--ZnbSmJtA6EgjuGCe--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v2] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 166 insertions(+), 1 deletion(-)
26.1% doc/src/sgml/
29.1% src/backend/utils/activity/
11.9% src/backend/utils/adt/
9.4% src/include/catalog/
3.9% src/include/utils/
3.3% src/include/
8.9% src/test/regress/expected/
7.1% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 3f4a27a736e..77b41e02b82 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..12ae9a8d321 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,23 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..d3491faaff2 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Xact statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--SywurcztURg2uk+W--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v3 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. A new function is called in AtEOXact_PgStat() to increment those
two new counters.
---
src/backend/utils/activity/pgstat_backend.c | 57 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
4 files changed, 69 insertions(+), 1 deletion(-)
78.9% src/backend/utils/activity/
11.2% src/include/utils/
9.8% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..bf164854c4b 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,20 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /* Count transaction commit or abort */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..9efc0e10ebf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
--
2.34.1
--u6+Li7FU79Mt/lVd
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v3-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v4 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..47ce61e5093 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -36,7 +36,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index b31f20d41bc..400a8c5d734 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -342,6 +342,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..0fdbaf79780 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -801,6 +809,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..b97184c6539 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,7 +616,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--jg9kqX3osLGO+8A7
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v4-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v5 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 7727fed3bda..42bfb9cd38f 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -37,7 +37,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -48,6 +48,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -261,6 +266,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -285,6 +318,10 @@ pgstat_flush_backend(bool nowait, uint32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -300,6 +337,9 @@ pgstat_flush_backend(bool nowait, uint32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index 933dcb5cae5..218ff5e653c 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -352,6 +352,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 8e3549c3752..5d73c4dfbcf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -523,6 +523,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -535,6 +537,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -844,6 +852,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index eed4c6b359c..8077c65e938 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -707,7 +707,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, uint32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--uN5gGi2rtCNSVbpC
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v5-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v4 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..47ce61e5093 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -36,7 +36,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index b31f20d41bc..400a8c5d734 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -342,6 +342,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..0fdbaf79780 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -801,6 +809,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..b97184c6539 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,7 +616,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--jg9kqX3osLGO+8A7
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v4-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v5 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 7727fed3bda..42bfb9cd38f 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -37,7 +37,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -48,6 +48,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -261,6 +266,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -285,6 +318,10 @@ pgstat_flush_backend(bool nowait, uint32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -300,6 +337,9 @@ pgstat_flush_backend(bool nowait, uint32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index 933dcb5cae5..218ff5e653c 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -352,6 +352,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 8e3549c3752..5d73c4dfbcf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -523,6 +523,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -535,6 +537,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -844,6 +852,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index eed4c6b359c..8077c65e938 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -707,7 +707,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, uint32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--uN5gGi2rtCNSVbpC
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v5-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v1] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 2 +
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 160 insertions(+), 1 deletion(-)
26.9% doc/src/sgml/
29.1% src/backend/utils/activity/
12.3% src/backend/utils/adt/
9.7% src/include/catalog/
4.0% src/include/utils/
9.2% src/test/regress/expected/
7.3% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index fa78031ccbb..ef89683164f 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..7cec0c83071 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,13 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static int pgStatBackendXactCommit = 0;
+static int pgStatBackendXactRollback = 0;
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +266,33 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += pgStatBackendXactCommit;
+ shbackendent->stats.xact_rollback += pgStatBackendXactRollback;
+
+ pgStatBackendXactCommit = pgStatBackendXactRollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +317,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +336,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +441,22 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ pgStatBackendXactCommit++;
+ else
+ pgStatBackendXactRollback++;
+
+ backend_has_xactstats = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..4c9f6b0dcec 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--ZnbSmJtA6EgjuGCe--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v2] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 166 insertions(+), 1 deletion(-)
26.1% doc/src/sgml/
29.1% src/backend/utils/activity/
11.9% src/backend/utils/adt/
9.4% src/include/catalog/
3.9% src/include/utils/
3.3% src/include/
8.9% src/test/regress/expected/
7.1% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 3f4a27a736e..77b41e02b82 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..12ae9a8d321 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,23 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..d3491faaff2 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Xact statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--SywurcztURg2uk+W--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v3 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. A new function is called in AtEOXact_PgStat() to increment those
two new counters.
---
src/backend/utils/activity/pgstat_backend.c | 57 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
4 files changed, 69 insertions(+), 1 deletion(-)
78.9% src/backend/utils/activity/
11.2% src/include/utils/
9.8% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..bf164854c4b 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,20 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /* Count transaction commit or abort */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..9efc0e10ebf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
--
2.34.1
--u6+Li7FU79Mt/lVd
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v3-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v4 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..47ce61e5093 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -36,7 +36,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index b31f20d41bc..400a8c5d734 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -342,6 +342,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..0fdbaf79780 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -801,6 +809,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..b97184c6539 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,7 +616,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--jg9kqX3osLGO+8A7
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v4-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v5 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 7727fed3bda..42bfb9cd38f 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -37,7 +37,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -48,6 +48,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -261,6 +266,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -285,6 +318,10 @@ pgstat_flush_backend(bool nowait, uint32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -300,6 +337,9 @@ pgstat_flush_backend(bool nowait, uint32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index 933dcb5cae5..218ff5e653c 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -352,6 +352,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 8e3549c3752..5d73c4dfbcf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -523,6 +523,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -535,6 +537,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -844,6 +852,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index eed4c6b359c..8077c65e938 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -707,7 +707,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, uint32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--uN5gGi2rtCNSVbpC
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v5-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v1] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 2 +
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 160 insertions(+), 1 deletion(-)
26.9% doc/src/sgml/
29.1% src/backend/utils/activity/
12.3% src/backend/utils/adt/
9.7% src/include/catalog/
4.0% src/include/utils/
9.2% src/test/regress/expected/
7.3% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index fa78031ccbb..ef89683164f 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..7cec0c83071 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,13 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static int pgStatBackendXactCommit = 0;
+static int pgStatBackendXactRollback = 0;
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +266,33 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += pgStatBackendXactCommit;
+ shbackendent->stats.xact_rollback += pgStatBackendXactRollback;
+
+ pgStatBackendXactCommit = pgStatBackendXactRollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +317,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +336,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +441,22 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ pgStatBackendXactCommit++;
+ else
+ pgStatBackendXactRollback++;
+
+ backend_has_xactstats = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..4c9f6b0dcec 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--ZnbSmJtA6EgjuGCe--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v2] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 166 insertions(+), 1 deletion(-)
26.1% doc/src/sgml/
29.1% src/backend/utils/activity/
11.9% src/backend/utils/adt/
9.4% src/include/catalog/
3.9% src/include/utils/
3.3% src/include/
8.9% src/test/regress/expected/
7.1% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 3f4a27a736e..77b41e02b82 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..12ae9a8d321 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,23 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..d3491faaff2 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Xact statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--SywurcztURg2uk+W--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v3 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. A new function is called in AtEOXact_PgStat() to increment those
two new counters.
---
src/backend/utils/activity/pgstat_backend.c | 57 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
4 files changed, 69 insertions(+), 1 deletion(-)
78.9% src/backend/utils/activity/
11.2% src/include/utils/
9.8% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..bf164854c4b 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,20 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /* Count transaction commit or abort */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..9efc0e10ebf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
--
2.34.1
--u6+Li7FU79Mt/lVd
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v3-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v4 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..47ce61e5093 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -36,7 +36,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index b31f20d41bc..400a8c5d734 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -342,6 +342,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..0fdbaf79780 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -801,6 +809,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..b97184c6539 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,7 +616,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--jg9kqX3osLGO+8A7
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v4-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v5 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 7727fed3bda..42bfb9cd38f 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -37,7 +37,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -48,6 +48,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -261,6 +266,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -285,6 +318,10 @@ pgstat_flush_backend(bool nowait, uint32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -300,6 +337,9 @@ pgstat_flush_backend(bool nowait, uint32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index 933dcb5cae5..218ff5e653c 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -352,6 +352,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 8e3549c3752..5d73c4dfbcf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -523,6 +523,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -535,6 +537,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -844,6 +852,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index eed4c6b359c..8077c65e938 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -707,7 +707,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, uint32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--uN5gGi2rtCNSVbpC
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v5-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v4 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..47ce61e5093 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -36,7 +36,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index b31f20d41bc..400a8c5d734 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -342,6 +342,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..0fdbaf79780 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -801,6 +809,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..b97184c6539 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,7 +616,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--jg9kqX3osLGO+8A7
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v4-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v1] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 2 +
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 160 insertions(+), 1 deletion(-)
26.9% doc/src/sgml/
29.1% src/backend/utils/activity/
12.3% src/backend/utils/adt/
9.7% src/include/catalog/
4.0% src/include/utils/
9.2% src/test/regress/expected/
7.3% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index fa78031ccbb..ef89683164f 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..7cec0c83071 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,13 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static int pgStatBackendXactCommit = 0;
+static int pgStatBackendXactRollback = 0;
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +266,33 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += pgStatBackendXactCommit;
+ shbackendent->stats.xact_rollback += pgStatBackendXactRollback;
+
+ pgStatBackendXactCommit = pgStatBackendXactRollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +317,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +336,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +441,22 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ pgStatBackendXactCommit++;
+ else
+ pgStatBackendXactRollback++;
+
+ backend_has_xactstats = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..4c9f6b0dcec 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--ZnbSmJtA6EgjuGCe--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v2] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 166 insertions(+), 1 deletion(-)
26.1% doc/src/sgml/
29.1% src/backend/utils/activity/
11.9% src/backend/utils/adt/
9.4% src/include/catalog/
3.9% src/include/utils/
3.3% src/include/
8.9% src/test/regress/expected/
7.1% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 3f4a27a736e..77b41e02b82 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..12ae9a8d321 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,23 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..d3491faaff2 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Xact statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--SywurcztURg2uk+W--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v3 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. A new function is called in AtEOXact_PgStat() to increment those
two new counters.
---
src/backend/utils/activity/pgstat_backend.c | 57 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
4 files changed, 69 insertions(+), 1 deletion(-)
78.9% src/backend/utils/activity/
11.2% src/include/utils/
9.8% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..bf164854c4b 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,20 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /* Count transaction commit or abort */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..9efc0e10ebf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
--
2.34.1
--u6+Li7FU79Mt/lVd
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v3-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v4 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..47ce61e5093 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -36,7 +36,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index b31f20d41bc..400a8c5d734 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -342,6 +342,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..0fdbaf79780 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -801,6 +809,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..b97184c6539 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,7 +616,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--jg9kqX3osLGO+8A7
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v4-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v5 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 7727fed3bda..42bfb9cd38f 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -37,7 +37,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -48,6 +48,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -261,6 +266,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -285,6 +318,10 @@ pgstat_flush_backend(bool nowait, uint32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -300,6 +337,9 @@ pgstat_flush_backend(bool nowait, uint32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index 933dcb5cae5..218ff5e653c 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -352,6 +352,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 8e3549c3752..5d73c4dfbcf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -523,6 +523,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -535,6 +537,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -844,6 +852,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index eed4c6b359c..8077c65e938 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -707,7 +707,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, uint32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--uN5gGi2rtCNSVbpC
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v5-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v1] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 2 +
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 160 insertions(+), 1 deletion(-)
26.9% doc/src/sgml/
29.1% src/backend/utils/activity/
12.3% src/backend/utils/adt/
9.7% src/include/catalog/
4.0% src/include/utils/
9.2% src/test/regress/expected/
7.3% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index fa78031ccbb..ef89683164f 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..7cec0c83071 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,13 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static int pgStatBackendXactCommit = 0;
+static int pgStatBackendXactRollback = 0;
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +266,33 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += pgStatBackendXactCommit;
+ shbackendent->stats.xact_rollback += pgStatBackendXactRollback;
+
+ pgStatBackendXactCommit = pgStatBackendXactRollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +317,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +336,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +441,22 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ pgStatBackendXactCommit++;
+ else
+ pgStatBackendXactRollback++;
+
+ backend_has_xactstats = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..4c9f6b0dcec 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--ZnbSmJtA6EgjuGCe--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v2] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 166 insertions(+), 1 deletion(-)
26.1% doc/src/sgml/
29.1% src/backend/utils/activity/
11.9% src/backend/utils/adt/
9.4% src/include/catalog/
3.9% src/include/utils/
3.3% src/include/
8.9% src/test/regress/expected/
7.1% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 3f4a27a736e..77b41e02b82 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..12ae9a8d321 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,23 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..d3491faaff2 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Xact statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--SywurcztURg2uk+W--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v3 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. A new function is called in AtEOXact_PgStat() to increment those
two new counters.
---
src/backend/utils/activity/pgstat_backend.c | 57 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
4 files changed, 69 insertions(+), 1 deletion(-)
78.9% src/backend/utils/activity/
11.2% src/include/utils/
9.8% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..bf164854c4b 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,20 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /* Count transaction commit or abort */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..9efc0e10ebf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
--
2.34.1
--u6+Li7FU79Mt/lVd
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v3-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v4 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..47ce61e5093 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -36,7 +36,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index b31f20d41bc..400a8c5d734 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -342,6 +342,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..0fdbaf79780 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -801,6 +809,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..b97184c6539 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,7 +616,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--jg9kqX3osLGO+8A7
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v4-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v5 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 7727fed3bda..42bfb9cd38f 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -37,7 +37,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -48,6 +48,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -261,6 +266,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -285,6 +318,10 @@ pgstat_flush_backend(bool nowait, uint32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -300,6 +337,9 @@ pgstat_flush_backend(bool nowait, uint32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index 933dcb5cae5..218ff5e653c 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -352,6 +352,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 8e3549c3752..5d73c4dfbcf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -523,6 +523,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -535,6 +537,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -844,6 +852,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index eed4c6b359c..8077c65e938 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -707,7 +707,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, uint32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--uN5gGi2rtCNSVbpC
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v5-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v1] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 2 +
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 160 insertions(+), 1 deletion(-)
26.9% doc/src/sgml/
29.1% src/backend/utils/activity/
12.3% src/backend/utils/adt/
9.7% src/include/catalog/
4.0% src/include/utils/
9.2% src/test/regress/expected/
7.3% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index fa78031ccbb..ef89683164f 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..7cec0c83071 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,13 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static int pgStatBackendXactCommit = 0;
+static int pgStatBackendXactRollback = 0;
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +266,33 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += pgStatBackendXactCommit;
+ shbackendent->stats.xact_rollback += pgStatBackendXactRollback;
+
+ pgStatBackendXactCommit = pgStatBackendXactRollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +317,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +336,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +441,22 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ pgStatBackendXactCommit++;
+ else
+ pgStatBackendXactRollback++;
+
+ backend_has_xactstats = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..4c9f6b0dcec 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--ZnbSmJtA6EgjuGCe--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v2] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 166 insertions(+), 1 deletion(-)
26.1% doc/src/sgml/
29.1% src/backend/utils/activity/
11.9% src/backend/utils/adt/
9.4% src/include/catalog/
3.9% src/include/utils/
3.3% src/include/
8.9% src/test/regress/expected/
7.1% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 3f4a27a736e..77b41e02b82 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..12ae9a8d321 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,23 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..d3491faaff2 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Xact statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--SywurcztURg2uk+W--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v3 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. A new function is called in AtEOXact_PgStat() to increment those
two new counters.
---
src/backend/utils/activity/pgstat_backend.c | 57 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
4 files changed, 69 insertions(+), 1 deletion(-)
78.9% src/backend/utils/activity/
11.2% src/include/utils/
9.8% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..bf164854c4b 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,20 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /* Count transaction commit or abort */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..9efc0e10ebf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
--
2.34.1
--u6+Li7FU79Mt/lVd
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v3-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v4 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..47ce61e5093 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -36,7 +36,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index b31f20d41bc..400a8c5d734 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -342,6 +342,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..0fdbaf79780 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -801,6 +809,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..b97184c6539 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,7 +616,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--jg9kqX3osLGO+8A7
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v4-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v5 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 7727fed3bda..42bfb9cd38f 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -37,7 +37,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -48,6 +48,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -261,6 +266,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -285,6 +318,10 @@ pgstat_flush_backend(bool nowait, uint32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -300,6 +337,9 @@ pgstat_flush_backend(bool nowait, uint32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index 933dcb5cae5..218ff5e653c 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -352,6 +352,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 8e3549c3752..5d73c4dfbcf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -523,6 +523,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -535,6 +537,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -844,6 +852,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index eed4c6b359c..8077c65e938 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -707,7 +707,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, uint32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--uN5gGi2rtCNSVbpC
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v5-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v4 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..47ce61e5093 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -36,7 +36,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index b31f20d41bc..400a8c5d734 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -342,6 +342,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..0fdbaf79780 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -801,6 +809,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..b97184c6539 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,7 +616,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--jg9kqX3osLGO+8A7
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v4-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v5 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 7727fed3bda..42bfb9cd38f 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -37,7 +37,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -48,6 +48,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -261,6 +266,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -285,6 +318,10 @@ pgstat_flush_backend(bool nowait, uint32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -300,6 +337,9 @@ pgstat_flush_backend(bool nowait, uint32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index 933dcb5cae5..218ff5e653c 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -352,6 +352,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 8e3549c3752..5d73c4dfbcf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -523,6 +523,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -535,6 +537,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -844,6 +852,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index eed4c6b359c..8077c65e938 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -707,7 +707,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, uint32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--uN5gGi2rtCNSVbpC
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v5-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v1] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 2 +
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 160 insertions(+), 1 deletion(-)
26.9% doc/src/sgml/
29.1% src/backend/utils/activity/
12.3% src/backend/utils/adt/
9.7% src/include/catalog/
4.0% src/include/utils/
9.2% src/test/regress/expected/
7.3% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index fa78031ccbb..ef89683164f 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..7cec0c83071 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,13 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static int pgStatBackendXactCommit = 0;
+static int pgStatBackendXactRollback = 0;
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +266,33 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += pgStatBackendXactCommit;
+ shbackendent->stats.xact_rollback += pgStatBackendXactRollback;
+
+ pgStatBackendXactCommit = pgStatBackendXactRollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +317,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +336,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +441,22 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ pgStatBackendXactCommit++;
+ else
+ pgStatBackendXactRollback++;
+
+ backend_has_xactstats = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..4c9f6b0dcec 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--ZnbSmJtA6EgjuGCe--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v2] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 166 insertions(+), 1 deletion(-)
26.1% doc/src/sgml/
29.1% src/backend/utils/activity/
11.9% src/backend/utils/adt/
9.4% src/include/catalog/
3.9% src/include/utils/
3.3% src/include/
8.9% src/test/regress/expected/
7.1% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 3f4a27a736e..77b41e02b82 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..12ae9a8d321 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,23 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..d3491faaff2 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Xact statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--SywurcztURg2uk+W--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v3 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. A new function is called in AtEOXact_PgStat() to increment those
two new counters.
---
src/backend/utils/activity/pgstat_backend.c | 57 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
4 files changed, 69 insertions(+), 1 deletion(-)
78.9% src/backend/utils/activity/
11.2% src/include/utils/
9.8% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..bf164854c4b 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,20 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /* Count transaction commit or abort */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..9efc0e10ebf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
--
2.34.1
--u6+Li7FU79Mt/lVd
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v3-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v4 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..47ce61e5093 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -36,7 +36,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index b31f20d41bc..400a8c5d734 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -342,6 +342,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..0fdbaf79780 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -801,6 +809,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..b97184c6539 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,7 +616,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--jg9kqX3osLGO+8A7
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v4-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v5 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 7727fed3bda..42bfb9cd38f 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -37,7 +37,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -48,6 +48,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -261,6 +266,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -285,6 +318,10 @@ pgstat_flush_backend(bool nowait, uint32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -300,6 +337,9 @@ pgstat_flush_backend(bool nowait, uint32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index 933dcb5cae5..218ff5e653c 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -352,6 +352,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 8e3549c3752..5d73c4dfbcf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -523,6 +523,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -535,6 +537,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -844,6 +852,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index eed4c6b359c..8077c65e938 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -707,7 +707,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, uint32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--uN5gGi2rtCNSVbpC
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v5-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v1] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 2 +
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 160 insertions(+), 1 deletion(-)
26.9% doc/src/sgml/
29.1% src/backend/utils/activity/
12.3% src/backend/utils/adt/
9.7% src/include/catalog/
4.0% src/include/utils/
9.2% src/test/regress/expected/
7.3% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index fa78031ccbb..ef89683164f 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..7cec0c83071 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,13 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static int pgStatBackendXactCommit = 0;
+static int pgStatBackendXactRollback = 0;
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +266,33 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += pgStatBackendXactCommit;
+ shbackendent->stats.xact_rollback += pgStatBackendXactRollback;
+
+ pgStatBackendXactCommit = pgStatBackendXactRollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +317,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +336,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +441,22 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ pgStatBackendXactCommit++;
+ else
+ pgStatBackendXactRollback++;
+
+ backend_has_xactstats = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..4c9f6b0dcec 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--ZnbSmJtA6EgjuGCe--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v2] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 166 insertions(+), 1 deletion(-)
26.1% doc/src/sgml/
29.1% src/backend/utils/activity/
11.9% src/backend/utils/adt/
9.4% src/include/catalog/
3.9% src/include/utils/
3.3% src/include/
8.9% src/test/regress/expected/
7.1% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 3f4a27a736e..77b41e02b82 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..12ae9a8d321 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,23 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..d3491faaff2 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Xact statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--SywurcztURg2uk+W--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v3 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. A new function is called in AtEOXact_PgStat() to increment those
two new counters.
---
src/backend/utils/activity/pgstat_backend.c | 57 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
4 files changed, 69 insertions(+), 1 deletion(-)
78.9% src/backend/utils/activity/
11.2% src/include/utils/
9.8% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..bf164854c4b 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,20 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /* Count transaction commit or abort */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..9efc0e10ebf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
--
2.34.1
--u6+Li7FU79Mt/lVd
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v3-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v5 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 7727fed3bda..42bfb9cd38f 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -37,7 +37,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -48,6 +48,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -261,6 +266,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -285,6 +318,10 @@ pgstat_flush_backend(bool nowait, uint32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -300,6 +337,9 @@ pgstat_flush_backend(bool nowait, uint32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index 933dcb5cae5..218ff5e653c 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -352,6 +352,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 8e3549c3752..5d73c4dfbcf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -523,6 +523,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -535,6 +537,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -844,6 +852,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index eed4c6b359c..8077c65e938 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -707,7 +707,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, uint32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--uN5gGi2rtCNSVbpC
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v5-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v1] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 2 +
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 160 insertions(+), 1 deletion(-)
26.9% doc/src/sgml/
29.1% src/backend/utils/activity/
12.3% src/backend/utils/adt/
9.7% src/include/catalog/
4.0% src/include/utils/
9.2% src/test/regress/expected/
7.3% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index fa78031ccbb..ef89683164f 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..7cec0c83071 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,13 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static int pgStatBackendXactCommit = 0;
+static int pgStatBackendXactRollback = 0;
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +266,33 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += pgStatBackendXactCommit;
+ shbackendent->stats.xact_rollback += pgStatBackendXactRollback;
+
+ pgStatBackendXactCommit = pgStatBackendXactRollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +317,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +336,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +441,22 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ pgStatBackendXactCommit++;
+ else
+ pgStatBackendXactRollback++;
+
+ backend_has_xactstats = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..4c9f6b0dcec 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--ZnbSmJtA6EgjuGCe--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v2] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 166 insertions(+), 1 deletion(-)
26.1% doc/src/sgml/
29.1% src/backend/utils/activity/
11.9% src/backend/utils/adt/
9.4% src/include/catalog/
3.9% src/include/utils/
3.3% src/include/
8.9% src/test/regress/expected/
7.1% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 3f4a27a736e..77b41e02b82 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..12ae9a8d321 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,23 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..d3491faaff2 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Xact statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--SywurcztURg2uk+W--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v3 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. A new function is called in AtEOXact_PgStat() to increment those
two new counters.
---
src/backend/utils/activity/pgstat_backend.c | 57 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
4 files changed, 69 insertions(+), 1 deletion(-)
78.9% src/backend/utils/activity/
11.2% src/include/utils/
9.8% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..bf164854c4b 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,20 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /* Count transaction commit or abort */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..9efc0e10ebf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
--
2.34.1
--u6+Li7FU79Mt/lVd
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v3-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v4 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..47ce61e5093 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -36,7 +36,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index b31f20d41bc..400a8c5d734 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -342,6 +342,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..0fdbaf79780 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -801,6 +809,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..b97184c6539 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,7 +616,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--jg9kqX3osLGO+8A7
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v4-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v5 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 7727fed3bda..42bfb9cd38f 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -37,7 +37,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -48,6 +48,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -261,6 +266,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -285,6 +318,10 @@ pgstat_flush_backend(bool nowait, uint32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -300,6 +337,9 @@ pgstat_flush_backend(bool nowait, uint32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index 933dcb5cae5..218ff5e653c 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -352,6 +352,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 8e3549c3752..5d73c4dfbcf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -523,6 +523,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -535,6 +537,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -844,6 +852,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index eed4c6b359c..8077c65e938 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -707,7 +707,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, uint32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--uN5gGi2rtCNSVbpC
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v5-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v1] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 2 +
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 160 insertions(+), 1 deletion(-)
26.9% doc/src/sgml/
29.1% src/backend/utils/activity/
12.3% src/backend/utils/adt/
9.7% src/include/catalog/
4.0% src/include/utils/
9.2% src/test/regress/expected/
7.3% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index fa78031ccbb..ef89683164f 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..7cec0c83071 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,13 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static int pgStatBackendXactCommit = 0;
+static int pgStatBackendXactRollback = 0;
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +266,33 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += pgStatBackendXactCommit;
+ shbackendent->stats.xact_rollback += pgStatBackendXactRollback;
+
+ pgStatBackendXactCommit = pgStatBackendXactRollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +317,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +336,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +441,22 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ pgStatBackendXactCommit++;
+ else
+ pgStatBackendXactRollback++;
+
+ backend_has_xactstats = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..4c9f6b0dcec 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--ZnbSmJtA6EgjuGCe--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v2] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 166 insertions(+), 1 deletion(-)
26.1% doc/src/sgml/
29.1% src/backend/utils/activity/
11.9% src/backend/utils/adt/
9.4% src/include/catalog/
3.9% src/include/utils/
3.3% src/include/
8.9% src/test/regress/expected/
7.1% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 3f4a27a736e..77b41e02b82 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..12ae9a8d321 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,23 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..d3491faaff2 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Xact statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--SywurcztURg2uk+W--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v3 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. A new function is called in AtEOXact_PgStat() to increment those
two new counters.
---
src/backend/utils/activity/pgstat_backend.c | 57 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
4 files changed, 69 insertions(+), 1 deletion(-)
78.9% src/backend/utils/activity/
11.2% src/include/utils/
9.8% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..bf164854c4b 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,20 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /* Count transaction commit or abort */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..9efc0e10ebf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
--
2.34.1
--u6+Li7FU79Mt/lVd
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v3-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v4 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..47ce61e5093 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -36,7 +36,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index b31f20d41bc..400a8c5d734 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -342,6 +342,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..0fdbaf79780 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -801,6 +809,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..b97184c6539 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,7 +616,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--jg9kqX3osLGO+8A7
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v4-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v5 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 7727fed3bda..42bfb9cd38f 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -37,7 +37,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -48,6 +48,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -261,6 +266,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -285,6 +318,10 @@ pgstat_flush_backend(bool nowait, uint32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -300,6 +337,9 @@ pgstat_flush_backend(bool nowait, uint32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index 933dcb5cae5..218ff5e653c 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -352,6 +352,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 8e3549c3752..5d73c4dfbcf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -523,6 +523,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -535,6 +537,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -844,6 +852,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index eed4c6b359c..8077c65e938 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -707,7 +707,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, uint32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--uN5gGi2rtCNSVbpC
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v5-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v1] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 2 +
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 160 insertions(+), 1 deletion(-)
26.9% doc/src/sgml/
29.1% src/backend/utils/activity/
12.3% src/backend/utils/adt/
9.7% src/include/catalog/
4.0% src/include/utils/
9.2% src/test/regress/expected/
7.3% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index fa78031ccbb..ef89683164f 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..7cec0c83071 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,13 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static int pgStatBackendXactCommit = 0;
+static int pgStatBackendXactRollback = 0;
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +266,33 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += pgStatBackendXactCommit;
+ shbackendent->stats.xact_rollback += pgStatBackendXactRollback;
+
+ pgStatBackendXactCommit = pgStatBackendXactRollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +317,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +336,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +441,22 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ pgStatBackendXactCommit++;
+ else
+ pgStatBackendXactRollback++;
+
+ backend_has_xactstats = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..4c9f6b0dcec 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--ZnbSmJtA6EgjuGCe--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v2] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 166 insertions(+), 1 deletion(-)
26.1% doc/src/sgml/
29.1% src/backend/utils/activity/
11.9% src/backend/utils/adt/
9.4% src/include/catalog/
3.9% src/include/utils/
3.3% src/include/
8.9% src/test/regress/expected/
7.1% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 3f4a27a736e..77b41e02b82 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..12ae9a8d321 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,23 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..d3491faaff2 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Xact statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--SywurcztURg2uk+W--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v3 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. A new function is called in AtEOXact_PgStat() to increment those
two new counters.
---
src/backend/utils/activity/pgstat_backend.c | 57 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
4 files changed, 69 insertions(+), 1 deletion(-)
78.9% src/backend/utils/activity/
11.2% src/include/utils/
9.8% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..bf164854c4b 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,20 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /* Count transaction commit or abort */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..9efc0e10ebf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
--
2.34.1
--u6+Li7FU79Mt/lVd
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v3-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v4 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..47ce61e5093 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -36,7 +36,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index b31f20d41bc..400a8c5d734 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -342,6 +342,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..0fdbaf79780 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -801,6 +809,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..b97184c6539 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,7 +616,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--jg9kqX3osLGO+8A7
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v4-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v5 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 7727fed3bda..42bfb9cd38f 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -37,7 +37,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -48,6 +48,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -261,6 +266,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -285,6 +318,10 @@ pgstat_flush_backend(bool nowait, uint32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -300,6 +337,9 @@ pgstat_flush_backend(bool nowait, uint32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index 933dcb5cae5..218ff5e653c 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -352,6 +352,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 8e3549c3752..5d73c4dfbcf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -523,6 +523,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -535,6 +537,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -844,6 +852,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index eed4c6b359c..8077c65e938 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -707,7 +707,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, uint32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--uN5gGi2rtCNSVbpC
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v5-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v4 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..47ce61e5093 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -36,7 +36,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index b31f20d41bc..400a8c5d734 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -342,6 +342,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..0fdbaf79780 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -801,6 +809,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..b97184c6539 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,7 +616,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--jg9kqX3osLGO+8A7
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v4-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v1] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 2 +
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 160 insertions(+), 1 deletion(-)
26.9% doc/src/sgml/
29.1% src/backend/utils/activity/
12.3% src/backend/utils/adt/
9.7% src/include/catalog/
4.0% src/include/utils/
9.2% src/test/regress/expected/
7.3% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index fa78031ccbb..ef89683164f 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..7cec0c83071 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,13 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static int pgStatBackendXactCommit = 0;
+static int pgStatBackendXactRollback = 0;
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +266,33 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += pgStatBackendXactCommit;
+ shbackendent->stats.xact_rollback += pgStatBackendXactRollback;
+
+ pgStatBackendXactCommit = pgStatBackendXactRollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +317,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +336,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +441,22 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ pgStatBackendXactCommit++;
+ else
+ pgStatBackendXactRollback++;
+
+ backend_has_xactstats = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..4c9f6b0dcec 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--ZnbSmJtA6EgjuGCe--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v2] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 166 insertions(+), 1 deletion(-)
26.1% doc/src/sgml/
29.1% src/backend/utils/activity/
11.9% src/backend/utils/adt/
9.4% src/include/catalog/
3.9% src/include/utils/
3.3% src/include/
8.9% src/test/regress/expected/
7.1% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 3f4a27a736e..77b41e02b82 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..12ae9a8d321 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,23 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..d3491faaff2 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Xact statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--SywurcztURg2uk+W--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v3 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. A new function is called in AtEOXact_PgStat() to increment those
two new counters.
---
src/backend/utils/activity/pgstat_backend.c | 57 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
4 files changed, 69 insertions(+), 1 deletion(-)
78.9% src/backend/utils/activity/
11.2% src/include/utils/
9.8% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..bf164854c4b 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,20 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /* Count transaction commit or abort */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..9efc0e10ebf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
--
2.34.1
--u6+Li7FU79Mt/lVd
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v3-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v4 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..47ce61e5093 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -36,7 +36,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index b31f20d41bc..400a8c5d734 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -342,6 +342,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..0fdbaf79780 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -801,6 +809,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..b97184c6539 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,7 +616,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--jg9kqX3osLGO+8A7
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v4-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v5 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 7727fed3bda..42bfb9cd38f 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -37,7 +37,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -48,6 +48,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -261,6 +266,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -285,6 +318,10 @@ pgstat_flush_backend(bool nowait, uint32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -300,6 +337,9 @@ pgstat_flush_backend(bool nowait, uint32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index 933dcb5cae5..218ff5e653c 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -352,6 +352,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 8e3549c3752..5d73c4dfbcf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -523,6 +523,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -535,6 +537,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -844,6 +852,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index eed4c6b359c..8077c65e938 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -707,7 +707,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, uint32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--uN5gGi2rtCNSVbpC
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v5-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v1] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 2 +
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 160 insertions(+), 1 deletion(-)
26.9% doc/src/sgml/
29.1% src/backend/utils/activity/
12.3% src/backend/utils/adt/
9.7% src/include/catalog/
4.0% src/include/utils/
9.2% src/test/regress/expected/
7.3% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index fa78031ccbb..ef89683164f 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..7cec0c83071 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,13 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static int pgStatBackendXactCommit = 0;
+static int pgStatBackendXactRollback = 0;
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +266,33 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += pgStatBackendXactCommit;
+ shbackendent->stats.xact_rollback += pgStatBackendXactRollback;
+
+ pgStatBackendXactCommit = pgStatBackendXactRollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +317,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +336,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +441,22 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ pgStatBackendXactCommit++;
+ else
+ pgStatBackendXactRollback++;
+
+ backend_has_xactstats = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..4c9f6b0dcec 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--ZnbSmJtA6EgjuGCe--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v2] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 166 insertions(+), 1 deletion(-)
26.1% doc/src/sgml/
29.1% src/backend/utils/activity/
11.9% src/backend/utils/adt/
9.4% src/include/catalog/
3.9% src/include/utils/
3.3% src/include/
8.9% src/test/regress/expected/
7.1% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 3f4a27a736e..77b41e02b82 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..12ae9a8d321 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,23 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..d3491faaff2 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Xact statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--SywurcztURg2uk+W--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v3 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. A new function is called in AtEOXact_PgStat() to increment those
two new counters.
---
src/backend/utils/activity/pgstat_backend.c | 57 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
4 files changed, 69 insertions(+), 1 deletion(-)
78.9% src/backend/utils/activity/
11.2% src/include/utils/
9.8% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..bf164854c4b 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,20 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /* Count transaction commit or abort */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..9efc0e10ebf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
--
2.34.1
--u6+Li7FU79Mt/lVd
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v3-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v4 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..47ce61e5093 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -36,7 +36,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index b31f20d41bc..400a8c5d734 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -342,6 +342,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..0fdbaf79780 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -801,6 +809,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..b97184c6539 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,7 +616,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--jg9kqX3osLGO+8A7
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v4-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v5 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 7727fed3bda..42bfb9cd38f 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -37,7 +37,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -48,6 +48,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -261,6 +266,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -285,6 +318,10 @@ pgstat_flush_backend(bool nowait, uint32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -300,6 +337,9 @@ pgstat_flush_backend(bool nowait, uint32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index 933dcb5cae5..218ff5e653c 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -352,6 +352,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 8e3549c3752..5d73c4dfbcf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -523,6 +523,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -535,6 +537,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -844,6 +852,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index eed4c6b359c..8077c65e938 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -707,7 +707,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, uint32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--uN5gGi2rtCNSVbpC
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v5-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v1] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 2 +
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 160 insertions(+), 1 deletion(-)
26.9% doc/src/sgml/
29.1% src/backend/utils/activity/
12.3% src/backend/utils/adt/
9.7% src/include/catalog/
4.0% src/include/utils/
9.2% src/test/regress/expected/
7.3% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index fa78031ccbb..ef89683164f 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..7cec0c83071 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,13 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static int pgStatBackendXactCommit = 0;
+static int pgStatBackendXactRollback = 0;
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +266,33 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += pgStatBackendXactCommit;
+ shbackendent->stats.xact_rollback += pgStatBackendXactRollback;
+
+ pgStatBackendXactCommit = pgStatBackendXactRollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +317,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +336,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +441,22 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ pgStatBackendXactCommit++;
+ else
+ pgStatBackendXactRollback++;
+
+ backend_has_xactstats = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..4c9f6b0dcec 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--ZnbSmJtA6EgjuGCe--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v2] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 166 insertions(+), 1 deletion(-)
26.1% doc/src/sgml/
29.1% src/backend/utils/activity/
11.9% src/backend/utils/adt/
9.4% src/include/catalog/
3.9% src/include/utils/
3.3% src/include/
8.9% src/test/regress/expected/
7.1% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 3f4a27a736e..77b41e02b82 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..12ae9a8d321 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,23 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..d3491faaff2 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Xact statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--SywurcztURg2uk+W--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v3 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. A new function is called in AtEOXact_PgStat() to increment those
two new counters.
---
src/backend/utils/activity/pgstat_backend.c | 57 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
4 files changed, 69 insertions(+), 1 deletion(-)
78.9% src/backend/utils/activity/
11.2% src/include/utils/
9.8% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..bf164854c4b 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,20 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /* Count transaction commit or abort */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..9efc0e10ebf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
--
2.34.1
--u6+Li7FU79Mt/lVd
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v3-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v5 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 7727fed3bda..42bfb9cd38f 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -37,7 +37,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -48,6 +48,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -261,6 +266,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -285,6 +318,10 @@ pgstat_flush_backend(bool nowait, uint32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -300,6 +337,9 @@ pgstat_flush_backend(bool nowait, uint32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index 933dcb5cae5..218ff5e653c 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -352,6 +352,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 8e3549c3752..5d73c4dfbcf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -523,6 +523,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -535,6 +537,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -844,6 +852,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index eed4c6b359c..8077c65e938 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -707,7 +707,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, uint32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--uN5gGi2rtCNSVbpC
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v5-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v1] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 2 +
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 160 insertions(+), 1 deletion(-)
26.9% doc/src/sgml/
29.1% src/backend/utils/activity/
12.3% src/backend/utils/adt/
9.7% src/include/catalog/
4.0% src/include/utils/
9.2% src/test/regress/expected/
7.3% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index fa78031ccbb..ef89683164f 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..7cec0c83071 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,13 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static int pgStatBackendXactCommit = 0;
+static int pgStatBackendXactRollback = 0;
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +266,33 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += pgStatBackendXactCommit;
+ shbackendent->stats.xact_rollback += pgStatBackendXactRollback;
+
+ pgStatBackendXactCommit = pgStatBackendXactRollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +317,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +336,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +441,22 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ pgStatBackendXactCommit++;
+ else
+ pgStatBackendXactRollback++;
+
+ backend_has_xactstats = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..4c9f6b0dcec 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--ZnbSmJtA6EgjuGCe--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v2] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 166 insertions(+), 1 deletion(-)
26.1% doc/src/sgml/
29.1% src/backend/utils/activity/
11.9% src/backend/utils/adt/
9.4% src/include/catalog/
3.9% src/include/utils/
3.3% src/include/
8.9% src/test/regress/expected/
7.1% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 3f4a27a736e..77b41e02b82 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..12ae9a8d321 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,23 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..d3491faaff2 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Xact statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--SywurcztURg2uk+W--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v3 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. A new function is called in AtEOXact_PgStat() to increment those
two new counters.
---
src/backend/utils/activity/pgstat_backend.c | 57 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
4 files changed, 69 insertions(+), 1 deletion(-)
78.9% src/backend/utils/activity/
11.2% src/include/utils/
9.8% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..bf164854c4b 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,20 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /* Count transaction commit or abort */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..9efc0e10ebf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
--
2.34.1
--u6+Li7FU79Mt/lVd
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v3-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v4 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..47ce61e5093 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -36,7 +36,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index b31f20d41bc..400a8c5d734 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -342,6 +342,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..0fdbaf79780 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -801,6 +809,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..b97184c6539 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,7 +616,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--jg9kqX3osLGO+8A7
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v4-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v5 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 7727fed3bda..42bfb9cd38f 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -37,7 +37,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -48,6 +48,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -261,6 +266,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -285,6 +318,10 @@ pgstat_flush_backend(bool nowait, uint32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -300,6 +337,9 @@ pgstat_flush_backend(bool nowait, uint32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index 933dcb5cae5..218ff5e653c 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -352,6 +352,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 8e3549c3752..5d73c4dfbcf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -523,6 +523,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -535,6 +537,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -844,6 +852,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index eed4c6b359c..8077c65e938 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -707,7 +707,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, uint32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--uN5gGi2rtCNSVbpC
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v5-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v1] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 2 +
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 160 insertions(+), 1 deletion(-)
26.9% doc/src/sgml/
29.1% src/backend/utils/activity/
12.3% src/backend/utils/adt/
9.7% src/include/catalog/
4.0% src/include/utils/
9.2% src/test/regress/expected/
7.3% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index fa78031ccbb..ef89683164f 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..7cec0c83071 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,13 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static int pgStatBackendXactCommit = 0;
+static int pgStatBackendXactRollback = 0;
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +266,33 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += pgStatBackendXactCommit;
+ shbackendent->stats.xact_rollback += pgStatBackendXactRollback;
+
+ pgStatBackendXactCommit = pgStatBackendXactRollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +317,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +336,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +441,22 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ pgStatBackendXactCommit++;
+ else
+ pgStatBackendXactRollback++;
+
+ backend_has_xactstats = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..4c9f6b0dcec 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--ZnbSmJtA6EgjuGCe--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v2] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 166 insertions(+), 1 deletion(-)
26.1% doc/src/sgml/
29.1% src/backend/utils/activity/
11.9% src/backend/utils/adt/
9.4% src/include/catalog/
3.9% src/include/utils/
3.3% src/include/
8.9% src/test/regress/expected/
7.1% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 3f4a27a736e..77b41e02b82 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..12ae9a8d321 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,23 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..d3491faaff2 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Xact statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--SywurcztURg2uk+W--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v3 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. A new function is called in AtEOXact_PgStat() to increment those
two new counters.
---
src/backend/utils/activity/pgstat_backend.c | 57 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
4 files changed, 69 insertions(+), 1 deletion(-)
78.9% src/backend/utils/activity/
11.2% src/include/utils/
9.8% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..bf164854c4b 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,20 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /* Count transaction commit or abort */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..9efc0e10ebf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
--
2.34.1
--u6+Li7FU79Mt/lVd
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v3-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v1] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 2 +
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 160 insertions(+), 1 deletion(-)
26.9% doc/src/sgml/
29.1% src/backend/utils/activity/
12.3% src/backend/utils/adt/
9.7% src/include/catalog/
4.0% src/include/utils/
9.2% src/test/regress/expected/
7.3% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index fa78031ccbb..ef89683164f 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..7cec0c83071 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,13 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static int pgStatBackendXactCommit = 0;
+static int pgStatBackendXactRollback = 0;
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +266,33 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += pgStatBackendXactCommit;
+ shbackendent->stats.xact_rollback += pgStatBackendXactRollback;
+
+ pgStatBackendXactCommit = pgStatBackendXactRollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +317,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +336,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +441,22 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ pgStatBackendXactCommit++;
+ else
+ pgStatBackendXactRollback++;
+
+ backend_has_xactstats = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..4c9f6b0dcec 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--ZnbSmJtA6EgjuGCe--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v2] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 166 insertions(+), 1 deletion(-)
26.1% doc/src/sgml/
29.1% src/backend/utils/activity/
11.9% src/backend/utils/adt/
9.4% src/include/catalog/
3.9% src/include/utils/
3.3% src/include/
8.9% src/test/regress/expected/
7.1% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 3f4a27a736e..77b41e02b82 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..12ae9a8d321 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,23 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..d3491faaff2 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Xact statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--SywurcztURg2uk+W--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v3 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. A new function is called in AtEOXact_PgStat() to increment those
two new counters.
---
src/backend/utils/activity/pgstat_backend.c | 57 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
4 files changed, 69 insertions(+), 1 deletion(-)
78.9% src/backend/utils/activity/
11.2% src/include/utils/
9.8% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..bf164854c4b 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,20 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /* Count transaction commit or abort */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..9efc0e10ebf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
--
2.34.1
--u6+Li7FU79Mt/lVd
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v3-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v4 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..47ce61e5093 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -36,7 +36,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index b31f20d41bc..400a8c5d734 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -342,6 +342,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..0fdbaf79780 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -801,6 +809,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..b97184c6539 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,7 +616,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--jg9kqX3osLGO+8A7
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v4-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v1] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 2 +
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 160 insertions(+), 1 deletion(-)
26.9% doc/src/sgml/
29.1% src/backend/utils/activity/
12.3% src/backend/utils/adt/
9.7% src/include/catalog/
4.0% src/include/utils/
9.2% src/test/regress/expected/
7.3% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index fa78031ccbb..ef89683164f 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..7cec0c83071 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,13 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static int pgStatBackendXactCommit = 0;
+static int pgStatBackendXactRollback = 0;
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +266,33 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += pgStatBackendXactCommit;
+ shbackendent->stats.xact_rollback += pgStatBackendXactRollback;
+
+ pgStatBackendXactCommit = pgStatBackendXactRollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +317,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +336,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +441,22 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ pgStatBackendXactCommit++;
+ else
+ pgStatBackendXactRollback++;
+
+ backend_has_xactstats = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..4c9f6b0dcec 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--ZnbSmJtA6EgjuGCe--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v2] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 166 insertions(+), 1 deletion(-)
26.1% doc/src/sgml/
29.1% src/backend/utils/activity/
11.9% src/backend/utils/adt/
9.4% src/include/catalog/
3.9% src/include/utils/
3.3% src/include/
8.9% src/test/regress/expected/
7.1% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 3f4a27a736e..77b41e02b82 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..12ae9a8d321 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,23 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..d3491faaff2 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Xact statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--SywurcztURg2uk+W--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v3 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. A new function is called in AtEOXact_PgStat() to increment those
two new counters.
---
src/backend/utils/activity/pgstat_backend.c | 57 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
4 files changed, 69 insertions(+), 1 deletion(-)
78.9% src/backend/utils/activity/
11.2% src/include/utils/
9.8% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..bf164854c4b 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,20 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /* Count transaction commit or abort */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..9efc0e10ebf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
--
2.34.1
--u6+Li7FU79Mt/lVd
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v3-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v4 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..47ce61e5093 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -36,7 +36,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index b31f20d41bc..400a8c5d734 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -342,6 +342,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..0fdbaf79780 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -801,6 +809,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..b97184c6539 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,7 +616,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--jg9kqX3osLGO+8A7
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v4-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v5 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 7727fed3bda..42bfb9cd38f 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -37,7 +37,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -48,6 +48,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -261,6 +266,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -285,6 +318,10 @@ pgstat_flush_backend(bool nowait, uint32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -300,6 +337,9 @@ pgstat_flush_backend(bool nowait, uint32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index 933dcb5cae5..218ff5e653c 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -352,6 +352,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 8e3549c3752..5d73c4dfbcf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -523,6 +523,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -535,6 +537,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -844,6 +852,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index eed4c6b359c..8077c65e938 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -707,7 +707,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, uint32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--uN5gGi2rtCNSVbpC
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v5-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v1] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 2 +
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 160 insertions(+), 1 deletion(-)
26.9% doc/src/sgml/
29.1% src/backend/utils/activity/
12.3% src/backend/utils/adt/
9.7% src/include/catalog/
4.0% src/include/utils/
9.2% src/test/regress/expected/
7.3% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index fa78031ccbb..ef89683164f 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..7cec0c83071 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,13 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static int pgStatBackendXactCommit = 0;
+static int pgStatBackendXactRollback = 0;
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +266,33 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += pgStatBackendXactCommit;
+ shbackendent->stats.xact_rollback += pgStatBackendXactRollback;
+
+ pgStatBackendXactCommit = pgStatBackendXactRollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +317,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +336,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +441,22 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ pgStatBackendXactCommit++;
+ else
+ pgStatBackendXactRollback++;
+
+ backend_has_xactstats = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..4c9f6b0dcec 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--ZnbSmJtA6EgjuGCe--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v2] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 166 insertions(+), 1 deletion(-)
26.1% doc/src/sgml/
29.1% src/backend/utils/activity/
11.9% src/backend/utils/adt/
9.4% src/include/catalog/
3.9% src/include/utils/
3.3% src/include/
8.9% src/test/regress/expected/
7.1% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 3f4a27a736e..77b41e02b82 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..12ae9a8d321 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,23 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..d3491faaff2 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Xact statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--SywurcztURg2uk+W--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v3 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. A new function is called in AtEOXact_PgStat() to increment those
two new counters.
---
src/backend/utils/activity/pgstat_backend.c | 57 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
4 files changed, 69 insertions(+), 1 deletion(-)
78.9% src/backend/utils/activity/
11.2% src/include/utils/
9.8% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..bf164854c4b 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,20 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /* Count transaction commit or abort */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..9efc0e10ebf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
--
2.34.1
--u6+Li7FU79Mt/lVd
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v3-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v4 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..47ce61e5093 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -36,7 +36,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index b31f20d41bc..400a8c5d734 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -342,6 +342,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..0fdbaf79780 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -801,6 +809,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..b97184c6539 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,7 +616,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--jg9kqX3osLGO+8A7
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v4-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v5 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 7727fed3bda..42bfb9cd38f 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -37,7 +37,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -48,6 +48,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -261,6 +266,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -285,6 +318,10 @@ pgstat_flush_backend(bool nowait, uint32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -300,6 +337,9 @@ pgstat_flush_backend(bool nowait, uint32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index 933dcb5cae5..218ff5e653c 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -352,6 +352,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 8e3549c3752..5d73c4dfbcf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -523,6 +523,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -535,6 +537,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -844,6 +852,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index eed4c6b359c..8077c65e938 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -707,7 +707,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, uint32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--uN5gGi2rtCNSVbpC
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v5-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v1] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 2 +
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 160 insertions(+), 1 deletion(-)
26.9% doc/src/sgml/
29.1% src/backend/utils/activity/
12.3% src/backend/utils/adt/
9.7% src/include/catalog/
4.0% src/include/utils/
9.2% src/test/regress/expected/
7.3% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index fa78031ccbb..ef89683164f 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..7cec0c83071 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,13 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static int pgStatBackendXactCommit = 0;
+static int pgStatBackendXactRollback = 0;
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +266,33 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += pgStatBackendXactCommit;
+ shbackendent->stats.xact_rollback += pgStatBackendXactRollback;
+
+ pgStatBackendXactCommit = pgStatBackendXactRollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +317,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +336,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +441,22 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ pgStatBackendXactCommit++;
+ else
+ pgStatBackendXactRollback++;
+
+ backend_has_xactstats = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..4c9f6b0dcec 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--ZnbSmJtA6EgjuGCe--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v2] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 166 insertions(+), 1 deletion(-)
26.1% doc/src/sgml/
29.1% src/backend/utils/activity/
11.9% src/backend/utils/adt/
9.4% src/include/catalog/
3.9% src/include/utils/
3.3% src/include/
8.9% src/test/regress/expected/
7.1% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 3f4a27a736e..77b41e02b82 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..12ae9a8d321 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,23 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..d3491faaff2 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Xact statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--SywurcztURg2uk+W--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v3 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. A new function is called in AtEOXact_PgStat() to increment those
two new counters.
---
src/backend/utils/activity/pgstat_backend.c | 57 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
4 files changed, 69 insertions(+), 1 deletion(-)
78.9% src/backend/utils/activity/
11.2% src/include/utils/
9.8% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..bf164854c4b 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,20 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /* Count transaction commit or abort */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..9efc0e10ebf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
--
2.34.1
--u6+Li7FU79Mt/lVd
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v3-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v4 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..47ce61e5093 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -36,7 +36,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index b31f20d41bc..400a8c5d734 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -342,6 +342,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..0fdbaf79780 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -801,6 +809,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..b97184c6539 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,7 +616,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--jg9kqX3osLGO+8A7
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v4-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v1] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 2 +
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 160 insertions(+), 1 deletion(-)
26.9% doc/src/sgml/
29.1% src/backend/utils/activity/
12.3% src/backend/utils/adt/
9.7% src/include/catalog/
4.0% src/include/utils/
9.2% src/test/regress/expected/
7.3% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index fa78031ccbb..ef89683164f 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..7cec0c83071 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,13 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static int pgStatBackendXactCommit = 0;
+static int pgStatBackendXactRollback = 0;
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +266,33 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += pgStatBackendXactCommit;
+ shbackendent->stats.xact_rollback += pgStatBackendXactRollback;
+
+ pgStatBackendXactCommit = pgStatBackendXactRollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +317,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +336,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +441,22 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ pgStatBackendXactCommit++;
+ else
+ pgStatBackendXactRollback++;
+
+ backend_has_xactstats = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..4c9f6b0dcec 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--ZnbSmJtA6EgjuGCe--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v2] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 166 insertions(+), 1 deletion(-)
26.1% doc/src/sgml/
29.1% src/backend/utils/activity/
11.9% src/backend/utils/adt/
9.4% src/include/catalog/
3.9% src/include/utils/
3.3% src/include/
8.9% src/test/regress/expected/
7.1% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 3f4a27a736e..77b41e02b82 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..12ae9a8d321 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,23 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..d3491faaff2 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Xact statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--SywurcztURg2uk+W--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v3 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. A new function is called in AtEOXact_PgStat() to increment those
two new counters.
---
src/backend/utils/activity/pgstat_backend.c | 57 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
4 files changed, 69 insertions(+), 1 deletion(-)
78.9% src/backend/utils/activity/
11.2% src/include/utils/
9.8% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..bf164854c4b 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,20 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /* Count transaction commit or abort */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..9efc0e10ebf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
--
2.34.1
--u6+Li7FU79Mt/lVd
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v3-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v4 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..47ce61e5093 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -36,7 +36,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index b31f20d41bc..400a8c5d734 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -342,6 +342,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..0fdbaf79780 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -801,6 +809,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..b97184c6539 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,7 +616,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--jg9kqX3osLGO+8A7
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v4-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v5 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 7727fed3bda..42bfb9cd38f 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -37,7 +37,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -48,6 +48,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -261,6 +266,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -285,6 +318,10 @@ pgstat_flush_backend(bool nowait, uint32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -300,6 +337,9 @@ pgstat_flush_backend(bool nowait, uint32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index 933dcb5cae5..218ff5e653c 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -352,6 +352,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 8e3549c3752..5d73c4dfbcf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -523,6 +523,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -535,6 +537,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -844,6 +852,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index eed4c6b359c..8077c65e938 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -707,7 +707,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, uint32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--uN5gGi2rtCNSVbpC
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v5-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v1] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 2 +
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 160 insertions(+), 1 deletion(-)
26.9% doc/src/sgml/
29.1% src/backend/utils/activity/
12.3% src/backend/utils/adt/
9.7% src/include/catalog/
4.0% src/include/utils/
9.2% src/test/regress/expected/
7.3% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index fa78031ccbb..ef89683164f 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..7cec0c83071 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,13 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static int pgStatBackendXactCommit = 0;
+static int pgStatBackendXactRollback = 0;
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +266,33 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += pgStatBackendXactCommit;
+ shbackendent->stats.xact_rollback += pgStatBackendXactRollback;
+
+ pgStatBackendXactCommit = pgStatBackendXactRollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +317,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +336,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +441,22 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ pgStatBackendXactCommit++;
+ else
+ pgStatBackendXactRollback++;
+
+ backend_has_xactstats = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..4c9f6b0dcec 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--ZnbSmJtA6EgjuGCe--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v2] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 166 insertions(+), 1 deletion(-)
26.1% doc/src/sgml/
29.1% src/backend/utils/activity/
11.9% src/backend/utils/adt/
9.4% src/include/catalog/
3.9% src/include/utils/
3.3% src/include/
8.9% src/test/regress/expected/
7.1% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 3f4a27a736e..77b41e02b82 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..12ae9a8d321 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,23 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..d3491faaff2 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Xact statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--SywurcztURg2uk+W--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v3 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. A new function is called in AtEOXact_PgStat() to increment those
two new counters.
---
src/backend/utils/activity/pgstat_backend.c | 57 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
4 files changed, 69 insertions(+), 1 deletion(-)
78.9% src/backend/utils/activity/
11.2% src/include/utils/
9.8% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..bf164854c4b 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,20 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /* Count transaction commit or abort */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..9efc0e10ebf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
--
2.34.1
--u6+Li7FU79Mt/lVd
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v3-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v4 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..47ce61e5093 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -36,7 +36,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index b31f20d41bc..400a8c5d734 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -342,6 +342,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..0fdbaf79780 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -801,6 +809,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..b97184c6539 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,7 +616,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--jg9kqX3osLGO+8A7
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v4-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v5 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 7727fed3bda..42bfb9cd38f 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -37,7 +37,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -48,6 +48,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -261,6 +266,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -285,6 +318,10 @@ pgstat_flush_backend(bool nowait, uint32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -300,6 +337,9 @@ pgstat_flush_backend(bool nowait, uint32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index 933dcb5cae5..218ff5e653c 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -352,6 +352,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 8e3549c3752..5d73c4dfbcf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -523,6 +523,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -535,6 +537,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -844,6 +852,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index eed4c6b359c..8077c65e938 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -707,7 +707,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, uint32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--uN5gGi2rtCNSVbpC
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v5-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v3 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. A new function is called in AtEOXact_PgStat() to increment those
two new counters.
---
src/backend/utils/activity/pgstat_backend.c | 57 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
4 files changed, 69 insertions(+), 1 deletion(-)
78.9% src/backend/utils/activity/
11.2% src/include/utils/
9.8% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..bf164854c4b 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,20 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /* Count transaction commit or abort */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..9efc0e10ebf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
--
2.34.1
--u6+Li7FU79Mt/lVd
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v3-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v1] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 2 +
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 160 insertions(+), 1 deletion(-)
26.9% doc/src/sgml/
29.1% src/backend/utils/activity/
12.3% src/backend/utils/adt/
9.7% src/include/catalog/
4.0% src/include/utils/
9.2% src/test/regress/expected/
7.3% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index fa78031ccbb..ef89683164f 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..7cec0c83071 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,13 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static int pgStatBackendXactCommit = 0;
+static int pgStatBackendXactRollback = 0;
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +266,33 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += pgStatBackendXactCommit;
+ shbackendent->stats.xact_rollback += pgStatBackendXactRollback;
+
+ pgStatBackendXactCommit = pgStatBackendXactRollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +317,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +336,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +441,22 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ pgStatBackendXactCommit++;
+ else
+ pgStatBackendXactRollback++;
+
+ backend_has_xactstats = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..4c9f6b0dcec 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--ZnbSmJtA6EgjuGCe--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v2] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 166 insertions(+), 1 deletion(-)
26.1% doc/src/sgml/
29.1% src/backend/utils/activity/
11.9% src/backend/utils/adt/
9.4% src/include/catalog/
3.9% src/include/utils/
3.3% src/include/
8.9% src/test/regress/expected/
7.1% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 3f4a27a736e..77b41e02b82 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..12ae9a8d321 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,23 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..d3491faaff2 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Xact statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--SywurcztURg2uk+W--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v3 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. A new function is called in AtEOXact_PgStat() to increment those
two new counters.
---
src/backend/utils/activity/pgstat_backend.c | 57 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
4 files changed, 69 insertions(+), 1 deletion(-)
78.9% src/backend/utils/activity/
11.2% src/include/utils/
9.8% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..bf164854c4b 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,20 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /* Count transaction commit or abort */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..9efc0e10ebf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
--
2.34.1
--u6+Li7FU79Mt/lVd
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v3-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v4 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..47ce61e5093 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -36,7 +36,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index b31f20d41bc..400a8c5d734 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -342,6 +342,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..0fdbaf79780 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -801,6 +809,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..b97184c6539 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,7 +616,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--jg9kqX3osLGO+8A7
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v4-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v5 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 7727fed3bda..42bfb9cd38f 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -37,7 +37,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -48,6 +48,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -261,6 +266,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -285,6 +318,10 @@ pgstat_flush_backend(bool nowait, uint32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -300,6 +337,9 @@ pgstat_flush_backend(bool nowait, uint32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index 933dcb5cae5..218ff5e653c 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -352,6 +352,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 8e3549c3752..5d73c4dfbcf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -523,6 +523,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -535,6 +537,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -844,6 +852,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index eed4c6b359c..8077c65e938 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -707,7 +707,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, uint32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--uN5gGi2rtCNSVbpC
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v5-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v1] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 2 +
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 160 insertions(+), 1 deletion(-)
26.9% doc/src/sgml/
29.1% src/backend/utils/activity/
12.3% src/backend/utils/adt/
9.7% src/include/catalog/
4.0% src/include/utils/
9.2% src/test/regress/expected/
7.3% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index fa78031ccbb..ef89683164f 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..7cec0c83071 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,13 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static int pgStatBackendXactCommit = 0;
+static int pgStatBackendXactRollback = 0;
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +266,33 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += pgStatBackendXactCommit;
+ shbackendent->stats.xact_rollback += pgStatBackendXactRollback;
+
+ pgStatBackendXactCommit = pgStatBackendXactRollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +317,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +336,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +441,22 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ pgStatBackendXactCommit++;
+ else
+ pgStatBackendXactRollback++;
+
+ backend_has_xactstats = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..4c9f6b0dcec 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--ZnbSmJtA6EgjuGCe--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v2] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 166 insertions(+), 1 deletion(-)
26.1% doc/src/sgml/
29.1% src/backend/utils/activity/
11.9% src/backend/utils/adt/
9.4% src/include/catalog/
3.9% src/include/utils/
3.3% src/include/
8.9% src/test/regress/expected/
7.1% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 3f4a27a736e..77b41e02b82 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..12ae9a8d321 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,23 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..d3491faaff2 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Xact statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--SywurcztURg2uk+W--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v3 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. A new function is called in AtEOXact_PgStat() to increment those
two new counters.
---
src/backend/utils/activity/pgstat_backend.c | 57 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
4 files changed, 69 insertions(+), 1 deletion(-)
78.9% src/backend/utils/activity/
11.2% src/include/utils/
9.8% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..bf164854c4b 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,20 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /* Count transaction commit or abort */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..9efc0e10ebf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
--
2.34.1
--u6+Li7FU79Mt/lVd
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v3-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v4 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..47ce61e5093 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -36,7 +36,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index b31f20d41bc..400a8c5d734 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -342,6 +342,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..0fdbaf79780 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -801,6 +809,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..b97184c6539 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,7 +616,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--jg9kqX3osLGO+8A7
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v4-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v5 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 7727fed3bda..42bfb9cd38f 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -37,7 +37,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -48,6 +48,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -261,6 +266,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -285,6 +318,10 @@ pgstat_flush_backend(bool nowait, uint32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -300,6 +337,9 @@ pgstat_flush_backend(bool nowait, uint32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index 933dcb5cae5..218ff5e653c 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -352,6 +352,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 8e3549c3752..5d73c4dfbcf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -523,6 +523,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -535,6 +537,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -844,6 +852,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index eed4c6b359c..8077c65e938 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -707,7 +707,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, uint32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--uN5gGi2rtCNSVbpC
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v5-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v1] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 2 +
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 160 insertions(+), 1 deletion(-)
26.9% doc/src/sgml/
29.1% src/backend/utils/activity/
12.3% src/backend/utils/adt/
9.7% src/include/catalog/
4.0% src/include/utils/
9.2% src/test/regress/expected/
7.3% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index fa78031ccbb..ef89683164f 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..7cec0c83071 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,13 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static int pgStatBackendXactCommit = 0;
+static int pgStatBackendXactRollback = 0;
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +266,33 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += pgStatBackendXactCommit;
+ shbackendent->stats.xact_rollback += pgStatBackendXactRollback;
+
+ pgStatBackendXactCommit = pgStatBackendXactRollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +317,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +336,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +441,22 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ pgStatBackendXactCommit++;
+ else
+ pgStatBackendXactRollback++;
+
+ backend_has_xactstats = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..4c9f6b0dcec 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--ZnbSmJtA6EgjuGCe--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v2] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 166 insertions(+), 1 deletion(-)
26.1% doc/src/sgml/
29.1% src/backend/utils/activity/
11.9% src/backend/utils/adt/
9.4% src/include/catalog/
3.9% src/include/utils/
3.3% src/include/
8.9% src/test/regress/expected/
7.1% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 3f4a27a736e..77b41e02b82 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..12ae9a8d321 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,23 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..d3491faaff2 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Xact statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--SywurcztURg2uk+W--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v3 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. A new function is called in AtEOXact_PgStat() to increment those
two new counters.
---
src/backend/utils/activity/pgstat_backend.c | 57 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
4 files changed, 69 insertions(+), 1 deletion(-)
78.9% src/backend/utils/activity/
11.2% src/include/utils/
9.8% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..bf164854c4b 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,20 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /* Count transaction commit or abort */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..9efc0e10ebf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
--
2.34.1
--u6+Li7FU79Mt/lVd
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v3-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v4 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..47ce61e5093 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -36,7 +36,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index b31f20d41bc..400a8c5d734 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -342,6 +342,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..0fdbaf79780 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -801,6 +809,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..b97184c6539 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,7 +616,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--jg9kqX3osLGO+8A7
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v4-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v5 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 7727fed3bda..42bfb9cd38f 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -37,7 +37,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -48,6 +48,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -261,6 +266,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -285,6 +318,10 @@ pgstat_flush_backend(bool nowait, uint32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -300,6 +337,9 @@ pgstat_flush_backend(bool nowait, uint32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index 933dcb5cae5..218ff5e653c 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -352,6 +352,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 8e3549c3752..5d73c4dfbcf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -523,6 +523,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -535,6 +537,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -844,6 +852,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index eed4c6b359c..8077c65e938 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -707,7 +707,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, uint32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--uN5gGi2rtCNSVbpC
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v5-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v1] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 2 +
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 160 insertions(+), 1 deletion(-)
26.9% doc/src/sgml/
29.1% src/backend/utils/activity/
12.3% src/backend/utils/adt/
9.7% src/include/catalog/
4.0% src/include/utils/
9.2% src/test/regress/expected/
7.3% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index fa78031ccbb..ef89683164f 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..7cec0c83071 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,13 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static int pgStatBackendXactCommit = 0;
+static int pgStatBackendXactRollback = 0;
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +266,33 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += pgStatBackendXactCommit;
+ shbackendent->stats.xact_rollback += pgStatBackendXactRollback;
+
+ pgStatBackendXactCommit = pgStatBackendXactRollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +317,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +336,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +441,22 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ pgStatBackendXactCommit++;
+ else
+ pgStatBackendXactRollback++;
+
+ backend_has_xactstats = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..4c9f6b0dcec 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--ZnbSmJtA6EgjuGCe--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v2] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 166 insertions(+), 1 deletion(-)
26.1% doc/src/sgml/
29.1% src/backend/utils/activity/
11.9% src/backend/utils/adt/
9.4% src/include/catalog/
3.9% src/include/utils/
3.3% src/include/
8.9% src/test/regress/expected/
7.1% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 3f4a27a736e..77b41e02b82 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..12ae9a8d321 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,23 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..d3491faaff2 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Xact statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--SywurcztURg2uk+W--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v3 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. A new function is called in AtEOXact_PgStat() to increment those
two new counters.
---
src/backend/utils/activity/pgstat_backend.c | 57 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
4 files changed, 69 insertions(+), 1 deletion(-)
78.9% src/backend/utils/activity/
11.2% src/include/utils/
9.8% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..bf164854c4b 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,20 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /* Count transaction commit or abort */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..9efc0e10ebf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
--
2.34.1
--u6+Li7FU79Mt/lVd
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v3-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v4 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..47ce61e5093 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -36,7 +36,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index b31f20d41bc..400a8c5d734 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -342,6 +342,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..0fdbaf79780 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -801,6 +809,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..b97184c6539 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,7 +616,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--jg9kqX3osLGO+8A7
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v4-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v5 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 7727fed3bda..42bfb9cd38f 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -37,7 +37,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -48,6 +48,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -261,6 +266,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -285,6 +318,10 @@ pgstat_flush_backend(bool nowait, uint32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -300,6 +337,9 @@ pgstat_flush_backend(bool nowait, uint32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index 933dcb5cae5..218ff5e653c 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -352,6 +352,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 8e3549c3752..5d73c4dfbcf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -523,6 +523,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -535,6 +537,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -844,6 +852,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index eed4c6b359c..8077c65e938 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -707,7 +707,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, uint32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--uN5gGi2rtCNSVbpC
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v5-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v5 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 7727fed3bda..42bfb9cd38f 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -37,7 +37,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -48,6 +48,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -261,6 +266,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -285,6 +318,10 @@ pgstat_flush_backend(bool nowait, uint32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -300,6 +337,9 @@ pgstat_flush_backend(bool nowait, uint32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index 933dcb5cae5..218ff5e653c 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -352,6 +352,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 8e3549c3752..5d73c4dfbcf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -523,6 +523,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -535,6 +537,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -844,6 +852,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index eed4c6b359c..8077c65e938 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -707,7 +707,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, uint32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--uN5gGi2rtCNSVbpC
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v5-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v2] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 166 insertions(+), 1 deletion(-)
26.1% doc/src/sgml/
29.1% src/backend/utils/activity/
11.9% src/backend/utils/adt/
9.4% src/include/catalog/
3.9% src/include/utils/
3.3% src/include/
8.9% src/test/regress/expected/
7.1% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 3f4a27a736e..77b41e02b82 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..12ae9a8d321 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,23 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..d3491faaff2 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Xact statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--SywurcztURg2uk+W--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v3 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. A new function is called in AtEOXact_PgStat() to increment those
two new counters.
---
src/backend/utils/activity/pgstat_backend.c | 57 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
4 files changed, 69 insertions(+), 1 deletion(-)
78.9% src/backend/utils/activity/
11.2% src/include/utils/
9.8% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..bf164854c4b 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,20 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /* Count transaction commit or abort */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..9efc0e10ebf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
--
2.34.1
--u6+Li7FU79Mt/lVd
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v3-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v4 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..47ce61e5093 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -36,7 +36,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index b31f20d41bc..400a8c5d734 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -342,6 +342,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..0fdbaf79780 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -801,6 +809,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..b97184c6539 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,7 +616,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--jg9kqX3osLGO+8A7
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v4-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v5 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 7727fed3bda..42bfb9cd38f 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -37,7 +37,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -48,6 +48,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -261,6 +266,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -285,6 +318,10 @@ pgstat_flush_backend(bool nowait, uint32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -300,6 +337,9 @@ pgstat_flush_backend(bool nowait, uint32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index 933dcb5cae5..218ff5e653c 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -352,6 +352,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 8e3549c3752..5d73c4dfbcf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -523,6 +523,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -535,6 +537,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -844,6 +852,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index eed4c6b359c..8077c65e938 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -707,7 +707,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, uint32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--uN5gGi2rtCNSVbpC
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v5-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v1] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 2 +
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 160 insertions(+), 1 deletion(-)
26.9% doc/src/sgml/
29.1% src/backend/utils/activity/
12.3% src/backend/utils/adt/
9.7% src/include/catalog/
4.0% src/include/utils/
9.2% src/test/regress/expected/
7.3% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index fa78031ccbb..ef89683164f 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..7cec0c83071 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,13 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static int pgStatBackendXactCommit = 0;
+static int pgStatBackendXactRollback = 0;
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +266,33 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += pgStatBackendXactCommit;
+ shbackendent->stats.xact_rollback += pgStatBackendXactRollback;
+
+ pgStatBackendXactCommit = pgStatBackendXactRollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +317,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +336,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +441,22 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ pgStatBackendXactCommit++;
+ else
+ pgStatBackendXactRollback++;
+
+ backend_has_xactstats = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..4c9f6b0dcec 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--ZnbSmJtA6EgjuGCe--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v2] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 166 insertions(+), 1 deletion(-)
26.1% doc/src/sgml/
29.1% src/backend/utils/activity/
11.9% src/backend/utils/adt/
9.4% src/include/catalog/
3.9% src/include/utils/
3.3% src/include/
8.9% src/test/regress/expected/
7.1% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 3f4a27a736e..77b41e02b82 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..12ae9a8d321 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,23 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..d3491faaff2 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Xact statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--SywurcztURg2uk+W--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v3 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. A new function is called in AtEOXact_PgStat() to increment those
two new counters.
---
src/backend/utils/activity/pgstat_backend.c | 57 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
4 files changed, 69 insertions(+), 1 deletion(-)
78.9% src/backend/utils/activity/
11.2% src/include/utils/
9.8% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..bf164854c4b 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,20 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /* Count transaction commit or abort */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..9efc0e10ebf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
--
2.34.1
--u6+Li7FU79Mt/lVd
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v3-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v4 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..47ce61e5093 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -36,7 +36,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index b31f20d41bc..400a8c5d734 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -342,6 +342,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..0fdbaf79780 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -801,6 +809,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..b97184c6539 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,7 +616,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--jg9kqX3osLGO+8A7
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v4-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v5 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 7727fed3bda..42bfb9cd38f 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -37,7 +37,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -48,6 +48,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -261,6 +266,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -285,6 +318,10 @@ pgstat_flush_backend(bool nowait, uint32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -300,6 +337,9 @@ pgstat_flush_backend(bool nowait, uint32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index 933dcb5cae5..218ff5e653c 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -352,6 +352,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 8e3549c3752..5d73c4dfbcf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -523,6 +523,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -535,6 +537,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -844,6 +852,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index eed4c6b359c..8077c65e938 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -707,7 +707,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, uint32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--uN5gGi2rtCNSVbpC
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v5-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v1] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 2 +
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 160 insertions(+), 1 deletion(-)
26.9% doc/src/sgml/
29.1% src/backend/utils/activity/
12.3% src/backend/utils/adt/
9.7% src/include/catalog/
4.0% src/include/utils/
9.2% src/test/regress/expected/
7.3% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index fa78031ccbb..ef89683164f 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..7cec0c83071 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,13 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static int pgStatBackendXactCommit = 0;
+static int pgStatBackendXactRollback = 0;
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +266,33 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += pgStatBackendXactCommit;
+ shbackendent->stats.xact_rollback += pgStatBackendXactRollback;
+
+ pgStatBackendXactCommit = pgStatBackendXactRollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +317,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +336,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +441,22 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ pgStatBackendXactCommit++;
+ else
+ pgStatBackendXactRollback++;
+
+ backend_has_xactstats = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..4c9f6b0dcec 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--ZnbSmJtA6EgjuGCe--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v2] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 166 insertions(+), 1 deletion(-)
26.1% doc/src/sgml/
29.1% src/backend/utils/activity/
11.9% src/backend/utils/adt/
9.4% src/include/catalog/
3.9% src/include/utils/
3.3% src/include/
8.9% src/test/regress/expected/
7.1% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 3f4a27a736e..77b41e02b82 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..12ae9a8d321 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,23 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..d3491faaff2 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Xact statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--SywurcztURg2uk+W--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v3 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. A new function is called in AtEOXact_PgStat() to increment those
two new counters.
---
src/backend/utils/activity/pgstat_backend.c | 57 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
4 files changed, 69 insertions(+), 1 deletion(-)
78.9% src/backend/utils/activity/
11.2% src/include/utils/
9.8% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..bf164854c4b 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,20 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /* Count transaction commit or abort */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..9efc0e10ebf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
--
2.34.1
--u6+Li7FU79Mt/lVd
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v3-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v4 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..47ce61e5093 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -36,7 +36,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index b31f20d41bc..400a8c5d734 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -342,6 +342,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..0fdbaf79780 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -801,6 +809,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..b97184c6539 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,7 +616,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--jg9kqX3osLGO+8A7
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v4-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v5 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 7727fed3bda..42bfb9cd38f 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -37,7 +37,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -48,6 +48,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -261,6 +266,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -285,6 +318,10 @@ pgstat_flush_backend(bool nowait, uint32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -300,6 +337,9 @@ pgstat_flush_backend(bool nowait, uint32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index 933dcb5cae5..218ff5e653c 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -352,6 +352,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 8e3549c3752..5d73c4dfbcf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -523,6 +523,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -535,6 +537,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -844,6 +852,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index eed4c6b359c..8077c65e938 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -707,7 +707,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, uint32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--uN5gGi2rtCNSVbpC
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v5-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v1] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 2 +
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 160 insertions(+), 1 deletion(-)
26.9% doc/src/sgml/
29.1% src/backend/utils/activity/
12.3% src/backend/utils/adt/
9.7% src/include/catalog/
4.0% src/include/utils/
9.2% src/test/regress/expected/
7.3% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index fa78031ccbb..ef89683164f 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..7cec0c83071 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,13 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static int pgStatBackendXactCommit = 0;
+static int pgStatBackendXactRollback = 0;
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +266,33 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += pgStatBackendXactCommit;
+ shbackendent->stats.xact_rollback += pgStatBackendXactRollback;
+
+ pgStatBackendXactCommit = pgStatBackendXactRollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +317,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +336,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +441,22 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ pgStatBackendXactCommit++;
+ else
+ pgStatBackendXactRollback++;
+
+ backend_has_xactstats = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..4c9f6b0dcec 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--ZnbSmJtA6EgjuGCe--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v2] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 166 insertions(+), 1 deletion(-)
26.1% doc/src/sgml/
29.1% src/backend/utils/activity/
11.9% src/backend/utils/adt/
9.4% src/include/catalog/
3.9% src/include/utils/
3.3% src/include/
8.9% src/test/regress/expected/
7.1% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 3f4a27a736e..77b41e02b82 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..12ae9a8d321 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,23 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..d3491faaff2 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Xact statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--SywurcztURg2uk+W--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v3 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. A new function is called in AtEOXact_PgStat() to increment those
two new counters.
---
src/backend/utils/activity/pgstat_backend.c | 57 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
4 files changed, 69 insertions(+), 1 deletion(-)
78.9% src/backend/utils/activity/
11.2% src/include/utils/
9.8% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..bf164854c4b 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,20 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /* Count transaction commit or abort */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..9efc0e10ebf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
--
2.34.1
--u6+Li7FU79Mt/lVd
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v3-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v4 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..47ce61e5093 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -36,7 +36,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index b31f20d41bc..400a8c5d734 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -342,6 +342,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..0fdbaf79780 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -801,6 +809,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..b97184c6539 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,7 +616,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--jg9kqX3osLGO+8A7
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v4-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v5 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 7727fed3bda..42bfb9cd38f 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -37,7 +37,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -48,6 +48,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -261,6 +266,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -285,6 +318,10 @@ pgstat_flush_backend(bool nowait, uint32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -300,6 +337,9 @@ pgstat_flush_backend(bool nowait, uint32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index 933dcb5cae5..218ff5e653c 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -352,6 +352,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 8e3549c3752..5d73c4dfbcf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -523,6 +523,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -535,6 +537,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -844,6 +852,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index eed4c6b359c..8077c65e938 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -707,7 +707,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, uint32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--uN5gGi2rtCNSVbpC
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v5-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v1] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 2 +
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 160 insertions(+), 1 deletion(-)
26.9% doc/src/sgml/
29.1% src/backend/utils/activity/
12.3% src/backend/utils/adt/
9.7% src/include/catalog/
4.0% src/include/utils/
9.2% src/test/regress/expected/
7.3% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index fa78031ccbb..ef89683164f 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..7cec0c83071 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,13 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static int pgStatBackendXactCommit = 0;
+static int pgStatBackendXactRollback = 0;
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +266,33 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += pgStatBackendXactCommit;
+ shbackendent->stats.xact_rollback += pgStatBackendXactRollback;
+
+ pgStatBackendXactCommit = pgStatBackendXactRollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +317,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +336,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +441,22 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ pgStatBackendXactCommit++;
+ else
+ pgStatBackendXactRollback++;
+
+ backend_has_xactstats = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..4c9f6b0dcec 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--ZnbSmJtA6EgjuGCe--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v2] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 166 insertions(+), 1 deletion(-)
26.1% doc/src/sgml/
29.1% src/backend/utils/activity/
11.9% src/backend/utils/adt/
9.4% src/include/catalog/
3.9% src/include/utils/
3.3% src/include/
8.9% src/test/regress/expected/
7.1% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 3f4a27a736e..77b41e02b82 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..12ae9a8d321 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,23 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..d3491faaff2 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Xact statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--SywurcztURg2uk+W--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v3 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. A new function is called in AtEOXact_PgStat() to increment those
two new counters.
---
src/backend/utils/activity/pgstat_backend.c | 57 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
4 files changed, 69 insertions(+), 1 deletion(-)
78.9% src/backend/utils/activity/
11.2% src/include/utils/
9.8% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..bf164854c4b 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,20 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /* Count transaction commit or abort */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..9efc0e10ebf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
--
2.34.1
--u6+Li7FU79Mt/lVd
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v3-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v4 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..47ce61e5093 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -36,7 +36,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index b31f20d41bc..400a8c5d734 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -342,6 +342,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..0fdbaf79780 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -801,6 +809,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..b97184c6539 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,7 +616,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--jg9kqX3osLGO+8A7
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v4-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v5 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 7727fed3bda..42bfb9cd38f 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -37,7 +37,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -48,6 +48,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -261,6 +266,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -285,6 +318,10 @@ pgstat_flush_backend(bool nowait, uint32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -300,6 +337,9 @@ pgstat_flush_backend(bool nowait, uint32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index 933dcb5cae5..218ff5e653c 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -352,6 +352,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 8e3549c3752..5d73c4dfbcf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -523,6 +523,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -535,6 +537,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -844,6 +852,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index eed4c6b359c..8077c65e938 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -707,7 +707,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, uint32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--uN5gGi2rtCNSVbpC
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v5-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v1] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 2 +
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 160 insertions(+), 1 deletion(-)
26.9% doc/src/sgml/
29.1% src/backend/utils/activity/
12.3% src/backend/utils/adt/
9.7% src/include/catalog/
4.0% src/include/utils/
9.2% src/test/regress/expected/
7.3% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index fa78031ccbb..ef89683164f 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..7cec0c83071 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,13 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static int pgStatBackendXactCommit = 0;
+static int pgStatBackendXactRollback = 0;
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +266,33 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += pgStatBackendXactCommit;
+ shbackendent->stats.xact_rollback += pgStatBackendXactRollback;
+
+ pgStatBackendXactCommit = pgStatBackendXactRollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +317,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +336,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +441,22 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ pgStatBackendXactCommit++;
+ else
+ pgStatBackendXactRollback++;
+
+ backend_has_xactstats = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..4c9f6b0dcec 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--ZnbSmJtA6EgjuGCe--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v2] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 166 insertions(+), 1 deletion(-)
26.1% doc/src/sgml/
29.1% src/backend/utils/activity/
11.9% src/backend/utils/adt/
9.4% src/include/catalog/
3.9% src/include/utils/
3.3% src/include/
8.9% src/test/regress/expected/
7.1% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 3f4a27a736e..77b41e02b82 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..12ae9a8d321 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,23 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..d3491faaff2 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Xact statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--SywurcztURg2uk+W--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v3 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. A new function is called in AtEOXact_PgStat() to increment those
two new counters.
---
src/backend/utils/activity/pgstat_backend.c | 57 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
4 files changed, 69 insertions(+), 1 deletion(-)
78.9% src/backend/utils/activity/
11.2% src/include/utils/
9.8% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..bf164854c4b 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,20 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /* Count transaction commit or abort */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..9efc0e10ebf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
--
2.34.1
--u6+Li7FU79Mt/lVd
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v3-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v4 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..47ce61e5093 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -36,7 +36,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index b31f20d41bc..400a8c5d734 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -342,6 +342,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..0fdbaf79780 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -801,6 +809,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..b97184c6539 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,7 +616,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--jg9kqX3osLGO+8A7
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v4-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v5 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 7727fed3bda..42bfb9cd38f 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -37,7 +37,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -48,6 +48,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -261,6 +266,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -285,6 +318,10 @@ pgstat_flush_backend(bool nowait, uint32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -300,6 +337,9 @@ pgstat_flush_backend(bool nowait, uint32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index 933dcb5cae5..218ff5e653c 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -352,6 +352,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 8e3549c3752..5d73c4dfbcf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -523,6 +523,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -535,6 +537,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -844,6 +852,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index eed4c6b359c..8077c65e938 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -707,7 +707,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, uint32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--uN5gGi2rtCNSVbpC
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v5-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v1] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 2 +
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 160 insertions(+), 1 deletion(-)
26.9% doc/src/sgml/
29.1% src/backend/utils/activity/
12.3% src/backend/utils/adt/
9.7% src/include/catalog/
4.0% src/include/utils/
9.2% src/test/regress/expected/
7.3% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index fa78031ccbb..ef89683164f 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..7cec0c83071 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,13 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static int pgStatBackendXactCommit = 0;
+static int pgStatBackendXactRollback = 0;
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +266,33 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += pgStatBackendXactCommit;
+ shbackendent->stats.xact_rollback += pgStatBackendXactRollback;
+
+ pgStatBackendXactCommit = pgStatBackendXactRollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +317,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +336,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +441,22 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ pgStatBackendXactCommit++;
+ else
+ pgStatBackendXactRollback++;
+
+ backend_has_xactstats = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..4c9f6b0dcec 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--ZnbSmJtA6EgjuGCe--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v2] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 166 insertions(+), 1 deletion(-)
26.1% doc/src/sgml/
29.1% src/backend/utils/activity/
11.9% src/backend/utils/adt/
9.4% src/include/catalog/
3.9% src/include/utils/
3.3% src/include/
8.9% src/test/regress/expected/
7.1% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 3f4a27a736e..77b41e02b82 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..12ae9a8d321 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,23 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..d3491faaff2 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Xact statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--SywurcztURg2uk+W--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v3 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. A new function is called in AtEOXact_PgStat() to increment those
two new counters.
---
src/backend/utils/activity/pgstat_backend.c | 57 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
4 files changed, 69 insertions(+), 1 deletion(-)
78.9% src/backend/utils/activity/
11.2% src/include/utils/
9.8% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..bf164854c4b 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,20 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /* Count transaction commit or abort */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..9efc0e10ebf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
--
2.34.1
--u6+Li7FU79Mt/lVd
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v3-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v4 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..47ce61e5093 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -36,7 +36,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index b31f20d41bc..400a8c5d734 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -342,6 +342,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..0fdbaf79780 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -801,6 +809,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..b97184c6539 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,7 +616,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--jg9kqX3osLGO+8A7
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v4-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v5 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 7727fed3bda..42bfb9cd38f 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -37,7 +37,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -48,6 +48,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -261,6 +266,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -285,6 +318,10 @@ pgstat_flush_backend(bool nowait, uint32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -300,6 +337,9 @@ pgstat_flush_backend(bool nowait, uint32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index 933dcb5cae5..218ff5e653c 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -352,6 +352,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 8e3549c3752..5d73c4dfbcf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -523,6 +523,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -535,6 +537,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -844,6 +852,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index eed4c6b359c..8077c65e938 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -707,7 +707,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, uint32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--uN5gGi2rtCNSVbpC
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v5-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v1] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 2 +
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 160 insertions(+), 1 deletion(-)
26.9% doc/src/sgml/
29.1% src/backend/utils/activity/
12.3% src/backend/utils/adt/
9.7% src/include/catalog/
4.0% src/include/utils/
9.2% src/test/regress/expected/
7.3% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index fa78031ccbb..ef89683164f 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..7cec0c83071 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,13 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static int pgStatBackendXactCommit = 0;
+static int pgStatBackendXactRollback = 0;
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +266,33 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += pgStatBackendXactCommit;
+ shbackendent->stats.xact_rollback += pgStatBackendXactRollback;
+
+ pgStatBackendXactCommit = pgStatBackendXactRollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +317,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +336,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +441,22 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ pgStatBackendXactCommit++;
+ else
+ pgStatBackendXactRollback++;
+
+ backend_has_xactstats = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..4c9f6b0dcec 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--ZnbSmJtA6EgjuGCe--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v2] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 166 insertions(+), 1 deletion(-)
26.1% doc/src/sgml/
29.1% src/backend/utils/activity/
11.9% src/backend/utils/adt/
9.4% src/include/catalog/
3.9% src/include/utils/
3.3% src/include/
8.9% src/test/regress/expected/
7.1% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 3f4a27a736e..77b41e02b82 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..12ae9a8d321 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,23 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..d3491faaff2 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Xact statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--SywurcztURg2uk+W--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v3 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. A new function is called in AtEOXact_PgStat() to increment those
two new counters.
---
src/backend/utils/activity/pgstat_backend.c | 57 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
4 files changed, 69 insertions(+), 1 deletion(-)
78.9% src/backend/utils/activity/
11.2% src/include/utils/
9.8% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..bf164854c4b 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,20 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /* Count transaction commit or abort */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..9efc0e10ebf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
--
2.34.1
--u6+Li7FU79Mt/lVd
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v3-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v4 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..47ce61e5093 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -36,7 +36,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index b31f20d41bc..400a8c5d734 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -342,6 +342,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..0fdbaf79780 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -801,6 +809,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..b97184c6539 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,7 +616,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--jg9kqX3osLGO+8A7
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v4-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v5 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 7727fed3bda..42bfb9cd38f 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -37,7 +37,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -48,6 +48,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -261,6 +266,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -285,6 +318,10 @@ pgstat_flush_backend(bool nowait, uint32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -300,6 +337,9 @@ pgstat_flush_backend(bool nowait, uint32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index 933dcb5cae5..218ff5e653c 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -352,6 +352,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 8e3549c3752..5d73c4dfbcf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -523,6 +523,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -535,6 +537,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -844,6 +852,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index eed4c6b359c..8077c65e938 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -707,7 +707,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, uint32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--uN5gGi2rtCNSVbpC
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v5-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v1] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 2 +
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 160 insertions(+), 1 deletion(-)
26.9% doc/src/sgml/
29.1% src/backend/utils/activity/
12.3% src/backend/utils/adt/
9.7% src/include/catalog/
4.0% src/include/utils/
9.2% src/test/regress/expected/
7.3% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index fa78031ccbb..ef89683164f 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..7cec0c83071 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,13 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static int pgStatBackendXactCommit = 0;
+static int pgStatBackendXactRollback = 0;
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +266,33 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += pgStatBackendXactCommit;
+ shbackendent->stats.xact_rollback += pgStatBackendXactRollback;
+
+ pgStatBackendXactCommit = pgStatBackendXactRollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +317,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +336,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +441,22 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ pgStatBackendXactCommit++;
+ else
+ pgStatBackendXactRollback++;
+
+ backend_has_xactstats = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..4c9f6b0dcec 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--ZnbSmJtA6EgjuGCe--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v2] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 166 insertions(+), 1 deletion(-)
26.1% doc/src/sgml/
29.1% src/backend/utils/activity/
11.9% src/backend/utils/adt/
9.4% src/include/catalog/
3.9% src/include/utils/
3.3% src/include/
8.9% src/test/regress/expected/
7.1% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 3f4a27a736e..77b41e02b82 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..12ae9a8d321 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,23 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..d3491faaff2 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Xact statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--SywurcztURg2uk+W--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v3 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. A new function is called in AtEOXact_PgStat() to increment those
two new counters.
---
src/backend/utils/activity/pgstat_backend.c | 57 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
4 files changed, 69 insertions(+), 1 deletion(-)
78.9% src/backend/utils/activity/
11.2% src/include/utils/
9.8% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..bf164854c4b 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,20 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /* Count transaction commit or abort */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..9efc0e10ebf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
--
2.34.1
--u6+Li7FU79Mt/lVd
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v3-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v4 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..47ce61e5093 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -36,7 +36,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index b31f20d41bc..400a8c5d734 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -342,6 +342,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..0fdbaf79780 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -801,6 +809,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..b97184c6539 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,7 +616,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--jg9kqX3osLGO+8A7
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v4-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v5 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 7727fed3bda..42bfb9cd38f 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -37,7 +37,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -48,6 +48,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -261,6 +266,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -285,6 +318,10 @@ pgstat_flush_backend(bool nowait, uint32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -300,6 +337,9 @@ pgstat_flush_backend(bool nowait, uint32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index 933dcb5cae5..218ff5e653c 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -352,6 +352,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 8e3549c3752..5d73c4dfbcf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -523,6 +523,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -535,6 +537,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -844,6 +852,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index eed4c6b359c..8077c65e938 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -707,7 +707,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, uint32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--uN5gGi2rtCNSVbpC
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v5-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v1] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 2 +
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 160 insertions(+), 1 deletion(-)
26.9% doc/src/sgml/
29.1% src/backend/utils/activity/
12.3% src/backend/utils/adt/
9.7% src/include/catalog/
4.0% src/include/utils/
9.2% src/test/regress/expected/
7.3% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index fa78031ccbb..ef89683164f 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..7cec0c83071 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,13 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static int pgStatBackendXactCommit = 0;
+static int pgStatBackendXactRollback = 0;
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +266,33 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += pgStatBackendXactCommit;
+ shbackendent->stats.xact_rollback += pgStatBackendXactRollback;
+
+ pgStatBackendXactCommit = pgStatBackendXactRollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +317,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +336,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +441,22 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ pgStatBackendXactCommit++;
+ else
+ pgStatBackendXactRollback++;
+
+ backend_has_xactstats = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..4c9f6b0dcec 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--ZnbSmJtA6EgjuGCe--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v2] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 166 insertions(+), 1 deletion(-)
26.1% doc/src/sgml/
29.1% src/backend/utils/activity/
11.9% src/backend/utils/adt/
9.4% src/include/catalog/
3.9% src/include/utils/
3.3% src/include/
8.9% src/test/regress/expected/
7.1% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 3f4a27a736e..77b41e02b82 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..12ae9a8d321 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,23 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..d3491faaff2 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Xact statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--SywurcztURg2uk+W--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v3 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. A new function is called in AtEOXact_PgStat() to increment those
two new counters.
---
src/backend/utils/activity/pgstat_backend.c | 57 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
4 files changed, 69 insertions(+), 1 deletion(-)
78.9% src/backend/utils/activity/
11.2% src/include/utils/
9.8% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..bf164854c4b 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,20 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /* Count transaction commit or abort */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..9efc0e10ebf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
--
2.34.1
--u6+Li7FU79Mt/lVd
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v3-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v4 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..47ce61e5093 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -36,7 +36,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index b31f20d41bc..400a8c5d734 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -342,6 +342,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..0fdbaf79780 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -801,6 +809,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..b97184c6539 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,7 +616,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--jg9kqX3osLGO+8A7
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v4-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v5 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 7727fed3bda..42bfb9cd38f 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -37,7 +37,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -48,6 +48,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -261,6 +266,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -285,6 +318,10 @@ pgstat_flush_backend(bool nowait, uint32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -300,6 +337,9 @@ pgstat_flush_backend(bool nowait, uint32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index 933dcb5cae5..218ff5e653c 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -352,6 +352,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 8e3549c3752..5d73c4dfbcf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -523,6 +523,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -535,6 +537,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -844,6 +852,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index eed4c6b359c..8077c65e938 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -707,7 +707,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, uint32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--uN5gGi2rtCNSVbpC
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v5-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v1] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 2 +
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 160 insertions(+), 1 deletion(-)
26.9% doc/src/sgml/
29.1% src/backend/utils/activity/
12.3% src/backend/utils/adt/
9.7% src/include/catalog/
4.0% src/include/utils/
9.2% src/test/regress/expected/
7.3% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index fa78031ccbb..ef89683164f 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..7cec0c83071 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,13 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static int pgStatBackendXactCommit = 0;
+static int pgStatBackendXactRollback = 0;
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +266,33 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += pgStatBackendXactCommit;
+ shbackendent->stats.xact_rollback += pgStatBackendXactRollback;
+
+ pgStatBackendXactCommit = pgStatBackendXactRollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +317,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +336,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +441,22 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ pgStatBackendXactCommit++;
+ else
+ pgStatBackendXactRollback++;
+
+ backend_has_xactstats = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..4c9f6b0dcec 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--ZnbSmJtA6EgjuGCe--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v2] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 166 insertions(+), 1 deletion(-)
26.1% doc/src/sgml/
29.1% src/backend/utils/activity/
11.9% src/backend/utils/adt/
9.4% src/include/catalog/
3.9% src/include/utils/
3.3% src/include/
8.9% src/test/regress/expected/
7.1% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 3f4a27a736e..77b41e02b82 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..12ae9a8d321 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,23 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..d3491faaff2 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Xact statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--SywurcztURg2uk+W--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v3 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. A new function is called in AtEOXact_PgStat() to increment those
two new counters.
---
src/backend/utils/activity/pgstat_backend.c | 57 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
4 files changed, 69 insertions(+), 1 deletion(-)
78.9% src/backend/utils/activity/
11.2% src/include/utils/
9.8% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..bf164854c4b 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,20 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /* Count transaction commit or abort */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..9efc0e10ebf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
--
2.34.1
--u6+Li7FU79Mt/lVd
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v3-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v4 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..47ce61e5093 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -36,7 +36,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index b31f20d41bc..400a8c5d734 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -342,6 +342,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..0fdbaf79780 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -801,6 +809,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..b97184c6539 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,7 +616,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--jg9kqX3osLGO+8A7
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v4-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v5 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 7727fed3bda..42bfb9cd38f 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -37,7 +37,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -48,6 +48,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -261,6 +266,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -285,6 +318,10 @@ pgstat_flush_backend(bool nowait, uint32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -300,6 +337,9 @@ pgstat_flush_backend(bool nowait, uint32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index 933dcb5cae5..218ff5e653c 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -352,6 +352,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 8e3549c3752..5d73c4dfbcf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -523,6 +523,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -535,6 +537,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -844,6 +852,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index eed4c6b359c..8077c65e938 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -707,7 +707,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, uint32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--uN5gGi2rtCNSVbpC
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v5-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v1] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 2 +
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 160 insertions(+), 1 deletion(-)
26.9% doc/src/sgml/
29.1% src/backend/utils/activity/
12.3% src/backend/utils/adt/
9.7% src/include/catalog/
4.0% src/include/utils/
9.2% src/test/regress/expected/
7.3% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index fa78031ccbb..ef89683164f 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..7cec0c83071 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,13 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static int pgStatBackendXactCommit = 0;
+static int pgStatBackendXactRollback = 0;
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +266,33 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += pgStatBackendXactCommit;
+ shbackendent->stats.xact_rollback += pgStatBackendXactRollback;
+
+ pgStatBackendXactCommit = pgStatBackendXactRollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +317,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +336,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +441,22 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ pgStatBackendXactCommit++;
+ else
+ pgStatBackendXactRollback++;
+
+ backend_has_xactstats = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..4c9f6b0dcec 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--ZnbSmJtA6EgjuGCe--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v2] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 166 insertions(+), 1 deletion(-)
26.1% doc/src/sgml/
29.1% src/backend/utils/activity/
11.9% src/backend/utils/adt/
9.4% src/include/catalog/
3.9% src/include/utils/
3.3% src/include/
8.9% src/test/regress/expected/
7.1% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 3f4a27a736e..77b41e02b82 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..12ae9a8d321 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,23 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..d3491faaff2 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Xact statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--SywurcztURg2uk+W--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v3 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. A new function is called in AtEOXact_PgStat() to increment those
two new counters.
---
src/backend/utils/activity/pgstat_backend.c | 57 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
4 files changed, 69 insertions(+), 1 deletion(-)
78.9% src/backend/utils/activity/
11.2% src/include/utils/
9.8% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..bf164854c4b 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,20 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /* Count transaction commit or abort */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..9efc0e10ebf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
--
2.34.1
--u6+Li7FU79Mt/lVd
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v3-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v4 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..47ce61e5093 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -36,7 +36,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index b31f20d41bc..400a8c5d734 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -342,6 +342,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..0fdbaf79780 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -801,6 +809,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..b97184c6539 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,7 +616,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--jg9kqX3osLGO+8A7
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v4-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v5 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 7727fed3bda..42bfb9cd38f 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -37,7 +37,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -48,6 +48,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -261,6 +266,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -285,6 +318,10 @@ pgstat_flush_backend(bool nowait, uint32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -300,6 +337,9 @@ pgstat_flush_backend(bool nowait, uint32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index 933dcb5cae5..218ff5e653c 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -352,6 +352,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 8e3549c3752..5d73c4dfbcf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -523,6 +523,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -535,6 +537,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -844,6 +852,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index eed4c6b359c..8077c65e938 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -707,7 +707,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, uint32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--uN5gGi2rtCNSVbpC
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v5-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v1] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 2 +
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 160 insertions(+), 1 deletion(-)
26.9% doc/src/sgml/
29.1% src/backend/utils/activity/
12.3% src/backend/utils/adt/
9.7% src/include/catalog/
4.0% src/include/utils/
9.2% src/test/regress/expected/
7.3% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index fa78031ccbb..ef89683164f 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..7cec0c83071 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,13 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static int pgStatBackendXactCommit = 0;
+static int pgStatBackendXactRollback = 0;
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +266,33 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += pgStatBackendXactCommit;
+ shbackendent->stats.xact_rollback += pgStatBackendXactRollback;
+
+ pgStatBackendXactCommit = pgStatBackendXactRollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +317,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +336,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +441,22 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ pgStatBackendXactCommit++;
+ else
+ pgStatBackendXactRollback++;
+
+ backend_has_xactstats = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..4c9f6b0dcec 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--ZnbSmJtA6EgjuGCe--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v2] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 166 insertions(+), 1 deletion(-)
26.1% doc/src/sgml/
29.1% src/backend/utils/activity/
11.9% src/backend/utils/adt/
9.4% src/include/catalog/
3.9% src/include/utils/
3.3% src/include/
8.9% src/test/regress/expected/
7.1% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 3f4a27a736e..77b41e02b82 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..12ae9a8d321 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,23 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..d3491faaff2 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Xact statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--SywurcztURg2uk+W--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v4 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..47ce61e5093 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -36,7 +36,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index b31f20d41bc..400a8c5d734 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -342,6 +342,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..0fdbaf79780 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -801,6 +809,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..b97184c6539 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,7 +616,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--jg9kqX3osLGO+8A7
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v4-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v5 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 7727fed3bda..42bfb9cd38f 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -37,7 +37,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -48,6 +48,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -261,6 +266,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -285,6 +318,10 @@ pgstat_flush_backend(bool nowait, uint32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -300,6 +337,9 @@ pgstat_flush_backend(bool nowait, uint32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index 933dcb5cae5..218ff5e653c 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -352,6 +352,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 8e3549c3752..5d73c4dfbcf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -523,6 +523,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -535,6 +537,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -844,6 +852,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index eed4c6b359c..8077c65e938 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -707,7 +707,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, uint32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--uN5gGi2rtCNSVbpC
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v5-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v1] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 2 +
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 160 insertions(+), 1 deletion(-)
26.9% doc/src/sgml/
29.1% src/backend/utils/activity/
12.3% src/backend/utils/adt/
9.7% src/include/catalog/
4.0% src/include/utils/
9.2% src/test/regress/expected/
7.3% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index fa78031ccbb..ef89683164f 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..7cec0c83071 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,13 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static int pgStatBackendXactCommit = 0;
+static int pgStatBackendXactRollback = 0;
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +266,33 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += pgStatBackendXactCommit;
+ shbackendent->stats.xact_rollback += pgStatBackendXactRollback;
+
+ pgStatBackendXactCommit = pgStatBackendXactRollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +317,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +336,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +441,22 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ pgStatBackendXactCommit++;
+ else
+ pgStatBackendXactRollback++;
+
+ backend_has_xactstats = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..4c9f6b0dcec 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--ZnbSmJtA6EgjuGCe--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v2] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 166 insertions(+), 1 deletion(-)
26.1% doc/src/sgml/
29.1% src/backend/utils/activity/
11.9% src/backend/utils/adt/
9.4% src/include/catalog/
3.9% src/include/utils/
3.3% src/include/
8.9% src/test/regress/expected/
7.1% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 3f4a27a736e..77b41e02b82 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..12ae9a8d321 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,23 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..d3491faaff2 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Xact statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--SywurcztURg2uk+W--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v3 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. A new function is called in AtEOXact_PgStat() to increment those
two new counters.
---
src/backend/utils/activity/pgstat_backend.c | 57 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
4 files changed, 69 insertions(+), 1 deletion(-)
78.9% src/backend/utils/activity/
11.2% src/include/utils/
9.8% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..bf164854c4b 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,20 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /* Count transaction commit or abort */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..9efc0e10ebf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
--
2.34.1
--u6+Li7FU79Mt/lVd
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v3-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v4 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..47ce61e5093 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -36,7 +36,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index b31f20d41bc..400a8c5d734 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -342,6 +342,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..0fdbaf79780 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -801,6 +809,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..b97184c6539 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,7 +616,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--jg9kqX3osLGO+8A7
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v4-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v5 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 7727fed3bda..42bfb9cd38f 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -37,7 +37,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -48,6 +48,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -261,6 +266,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -285,6 +318,10 @@ pgstat_flush_backend(bool nowait, uint32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -300,6 +337,9 @@ pgstat_flush_backend(bool nowait, uint32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index 933dcb5cae5..218ff5e653c 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -352,6 +352,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 8e3549c3752..5d73c4dfbcf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -523,6 +523,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -535,6 +537,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -844,6 +852,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index eed4c6b359c..8077c65e938 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -707,7 +707,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, uint32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--uN5gGi2rtCNSVbpC
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v5-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v1] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 2 +
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 160 insertions(+), 1 deletion(-)
26.9% doc/src/sgml/
29.1% src/backend/utils/activity/
12.3% src/backend/utils/adt/
9.7% src/include/catalog/
4.0% src/include/utils/
9.2% src/test/regress/expected/
7.3% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index fa78031ccbb..ef89683164f 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..7cec0c83071 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,13 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static int pgStatBackendXactCommit = 0;
+static int pgStatBackendXactRollback = 0;
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +266,33 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += pgStatBackendXactCommit;
+ shbackendent->stats.xact_rollback += pgStatBackendXactRollback;
+
+ pgStatBackendXactCommit = pgStatBackendXactRollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +317,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +336,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +441,22 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ pgStatBackendXactCommit++;
+ else
+ pgStatBackendXactRollback++;
+
+ backend_has_xactstats = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..4c9f6b0dcec 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--ZnbSmJtA6EgjuGCe--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v2] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 166 insertions(+), 1 deletion(-)
26.1% doc/src/sgml/
29.1% src/backend/utils/activity/
11.9% src/backend/utils/adt/
9.4% src/include/catalog/
3.9% src/include/utils/
3.3% src/include/
8.9% src/test/regress/expected/
7.1% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 3f4a27a736e..77b41e02b82 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..12ae9a8d321 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,23 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..d3491faaff2 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Xact statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--SywurcztURg2uk+W--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v3 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. A new function is called in AtEOXact_PgStat() to increment those
two new counters.
---
src/backend/utils/activity/pgstat_backend.c | 57 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
4 files changed, 69 insertions(+), 1 deletion(-)
78.9% src/backend/utils/activity/
11.2% src/include/utils/
9.8% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..bf164854c4b 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,20 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /* Count transaction commit or abort */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..9efc0e10ebf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
--
2.34.1
--u6+Li7FU79Mt/lVd
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v3-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v4 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..47ce61e5093 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -36,7 +36,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index b31f20d41bc..400a8c5d734 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -342,6 +342,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..0fdbaf79780 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -801,6 +809,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..b97184c6539 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,7 +616,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--jg9kqX3osLGO+8A7
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v4-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v5 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 7727fed3bda..42bfb9cd38f 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -37,7 +37,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -48,6 +48,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -261,6 +266,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -285,6 +318,10 @@ pgstat_flush_backend(bool nowait, uint32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -300,6 +337,9 @@ pgstat_flush_backend(bool nowait, uint32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index 933dcb5cae5..218ff5e653c 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -352,6 +352,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 8e3549c3752..5d73c4dfbcf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -523,6 +523,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -535,6 +537,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -844,6 +852,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index eed4c6b359c..8077c65e938 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -707,7 +707,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, uint32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--uN5gGi2rtCNSVbpC
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v5-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v1] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 2 +
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 160 insertions(+), 1 deletion(-)
26.9% doc/src/sgml/
29.1% src/backend/utils/activity/
12.3% src/backend/utils/adt/
9.7% src/include/catalog/
4.0% src/include/utils/
9.2% src/test/regress/expected/
7.3% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index fa78031ccbb..ef89683164f 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..7cec0c83071 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,13 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static int pgStatBackendXactCommit = 0;
+static int pgStatBackendXactRollback = 0;
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +266,33 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += pgStatBackendXactCommit;
+ shbackendent->stats.xact_rollback += pgStatBackendXactRollback;
+
+ pgStatBackendXactCommit = pgStatBackendXactRollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +317,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +336,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +441,22 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ pgStatBackendXactCommit++;
+ else
+ pgStatBackendXactRollback++;
+
+ backend_has_xactstats = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..4c9f6b0dcec 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--ZnbSmJtA6EgjuGCe--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v2] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 166 insertions(+), 1 deletion(-)
26.1% doc/src/sgml/
29.1% src/backend/utils/activity/
11.9% src/backend/utils/adt/
9.4% src/include/catalog/
3.9% src/include/utils/
3.3% src/include/
8.9% src/test/regress/expected/
7.1% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 3f4a27a736e..77b41e02b82 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..12ae9a8d321 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,23 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..d3491faaff2 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Xact statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--SywurcztURg2uk+W--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v4 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..47ce61e5093 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -36,7 +36,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index b31f20d41bc..400a8c5d734 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -342,6 +342,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..0fdbaf79780 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -801,6 +809,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..b97184c6539 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,7 +616,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--jg9kqX3osLGO+8A7
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v4-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v5 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 7727fed3bda..42bfb9cd38f 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -37,7 +37,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -48,6 +48,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -261,6 +266,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -285,6 +318,10 @@ pgstat_flush_backend(bool nowait, uint32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -300,6 +337,9 @@ pgstat_flush_backend(bool nowait, uint32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index 933dcb5cae5..218ff5e653c 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -352,6 +352,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 8e3549c3752..5d73c4dfbcf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -523,6 +523,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -535,6 +537,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -844,6 +852,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index eed4c6b359c..8077c65e938 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -707,7 +707,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, uint32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--uN5gGi2rtCNSVbpC
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v5-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v2] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 166 insertions(+), 1 deletion(-)
26.1% doc/src/sgml/
29.1% src/backend/utils/activity/
11.9% src/backend/utils/adt/
9.4% src/include/catalog/
3.9% src/include/utils/
3.3% src/include/
8.9% src/test/regress/expected/
7.1% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 3f4a27a736e..77b41e02b82 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..12ae9a8d321 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,23 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..d3491faaff2 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Xact statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--SywurcztURg2uk+W--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v3 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. A new function is called in AtEOXact_PgStat() to increment those
two new counters.
---
src/backend/utils/activity/pgstat_backend.c | 57 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
4 files changed, 69 insertions(+), 1 deletion(-)
78.9% src/backend/utils/activity/
11.2% src/include/utils/
9.8% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..bf164854c4b 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,20 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /* Count transaction commit or abort */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..9efc0e10ebf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
--
2.34.1
--u6+Li7FU79Mt/lVd
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v3-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v4 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..47ce61e5093 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -36,7 +36,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index b31f20d41bc..400a8c5d734 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -342,6 +342,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..0fdbaf79780 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -801,6 +809,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..b97184c6539 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,7 +616,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--jg9kqX3osLGO+8A7
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v4-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v4 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..47ce61e5093 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -36,7 +36,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index b31f20d41bc..400a8c5d734 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -342,6 +342,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..0fdbaf79780 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -801,6 +809,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..b97184c6539 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,7 +616,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--jg9kqX3osLGO+8A7
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v4-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v5 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 7727fed3bda..42bfb9cd38f 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -37,7 +37,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -48,6 +48,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -261,6 +266,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -285,6 +318,10 @@ pgstat_flush_backend(bool nowait, uint32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -300,6 +337,9 @@ pgstat_flush_backend(bool nowait, uint32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index 933dcb5cae5..218ff5e653c 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -352,6 +352,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 8e3549c3752..5d73c4dfbcf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -523,6 +523,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -535,6 +537,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -844,6 +852,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index eed4c6b359c..8077c65e938 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -707,7 +707,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, uint32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--uN5gGi2rtCNSVbpC
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v5-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v1] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 2 +
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 160 insertions(+), 1 deletion(-)
26.9% doc/src/sgml/
29.1% src/backend/utils/activity/
12.3% src/backend/utils/adt/
9.7% src/include/catalog/
4.0% src/include/utils/
9.2% src/test/regress/expected/
7.3% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index fa78031ccbb..ef89683164f 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..7cec0c83071 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,13 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static int pgStatBackendXactCommit = 0;
+static int pgStatBackendXactRollback = 0;
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +266,33 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += pgStatBackendXactCommit;
+ shbackendent->stats.xact_rollback += pgStatBackendXactRollback;
+
+ pgStatBackendXactCommit = pgStatBackendXactRollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +317,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +336,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +441,22 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ pgStatBackendXactCommit++;
+ else
+ pgStatBackendXactRollback++;
+
+ backend_has_xactstats = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..4c9f6b0dcec 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--ZnbSmJtA6EgjuGCe--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v2] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 166 insertions(+), 1 deletion(-)
26.1% doc/src/sgml/
29.1% src/backend/utils/activity/
11.9% src/backend/utils/adt/
9.4% src/include/catalog/
3.9% src/include/utils/
3.3% src/include/
8.9% src/test/regress/expected/
7.1% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 3f4a27a736e..77b41e02b82 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..12ae9a8d321 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,23 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..d3491faaff2 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Xact statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--SywurcztURg2uk+W--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v3 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. A new function is called in AtEOXact_PgStat() to increment those
two new counters.
---
src/backend/utils/activity/pgstat_backend.c | 57 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
4 files changed, 69 insertions(+), 1 deletion(-)
78.9% src/backend/utils/activity/
11.2% src/include/utils/
9.8% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..bf164854c4b 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,20 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /* Count transaction commit or abort */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..9efc0e10ebf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
--
2.34.1
--u6+Li7FU79Mt/lVd
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v3-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v4 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..47ce61e5093 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -36,7 +36,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index b31f20d41bc..400a8c5d734 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -342,6 +342,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..0fdbaf79780 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -801,6 +809,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..b97184c6539 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,7 +616,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--jg9kqX3osLGO+8A7
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v4-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v5 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 7727fed3bda..42bfb9cd38f 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -37,7 +37,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -48,6 +48,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -261,6 +266,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -285,6 +318,10 @@ pgstat_flush_backend(bool nowait, uint32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -300,6 +337,9 @@ pgstat_flush_backend(bool nowait, uint32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index 933dcb5cae5..218ff5e653c 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -352,6 +352,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 8e3549c3752..5d73c4dfbcf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -523,6 +523,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -535,6 +537,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -844,6 +852,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index eed4c6b359c..8077c65e938 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -707,7 +707,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, uint32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--uN5gGi2rtCNSVbpC
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v5-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v1] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 2 +
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 160 insertions(+), 1 deletion(-)
26.9% doc/src/sgml/
29.1% src/backend/utils/activity/
12.3% src/backend/utils/adt/
9.7% src/include/catalog/
4.0% src/include/utils/
9.2% src/test/regress/expected/
7.3% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index fa78031ccbb..ef89683164f 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..7cec0c83071 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,13 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static int pgStatBackendXactCommit = 0;
+static int pgStatBackendXactRollback = 0;
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +266,33 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += pgStatBackendXactCommit;
+ shbackendent->stats.xact_rollback += pgStatBackendXactRollback;
+
+ pgStatBackendXactCommit = pgStatBackendXactRollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +317,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +336,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +441,22 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ pgStatBackendXactCommit++;
+ else
+ pgStatBackendXactRollback++;
+
+ backend_has_xactstats = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..4c9f6b0dcec 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--ZnbSmJtA6EgjuGCe--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v2] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 166 insertions(+), 1 deletion(-)
26.1% doc/src/sgml/
29.1% src/backend/utils/activity/
11.9% src/backend/utils/adt/
9.4% src/include/catalog/
3.9% src/include/utils/
3.3% src/include/
8.9% src/test/regress/expected/
7.1% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 3f4a27a736e..77b41e02b82 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..12ae9a8d321 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,23 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..d3491faaff2 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Xact statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--SywurcztURg2uk+W--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v3 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. A new function is called in AtEOXact_PgStat() to increment those
two new counters.
---
src/backend/utils/activity/pgstat_backend.c | 57 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
4 files changed, 69 insertions(+), 1 deletion(-)
78.9% src/backend/utils/activity/
11.2% src/include/utils/
9.8% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..bf164854c4b 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,20 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /* Count transaction commit or abort */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..9efc0e10ebf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
--
2.34.1
--u6+Li7FU79Mt/lVd
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v3-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v4 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..47ce61e5093 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -36,7 +36,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index b31f20d41bc..400a8c5d734 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -342,6 +342,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..0fdbaf79780 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -801,6 +809,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..b97184c6539 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,7 +616,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--jg9kqX3osLGO+8A7
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v4-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v1] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 2 +
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 160 insertions(+), 1 deletion(-)
26.9% doc/src/sgml/
29.1% src/backend/utils/activity/
12.3% src/backend/utils/adt/
9.7% src/include/catalog/
4.0% src/include/utils/
9.2% src/test/regress/expected/
7.3% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index fa78031ccbb..ef89683164f 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..7cec0c83071 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,13 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static int pgStatBackendXactCommit = 0;
+static int pgStatBackendXactRollback = 0;
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +266,33 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += pgStatBackendXactCommit;
+ shbackendent->stats.xact_rollback += pgStatBackendXactRollback;
+
+ pgStatBackendXactCommit = pgStatBackendXactRollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +317,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +336,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +441,22 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ pgStatBackendXactCommit++;
+ else
+ pgStatBackendXactRollback++;
+
+ backend_has_xactstats = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..4c9f6b0dcec 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--ZnbSmJtA6EgjuGCe--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v2] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 166 insertions(+), 1 deletion(-)
26.1% doc/src/sgml/
29.1% src/backend/utils/activity/
11.9% src/backend/utils/adt/
9.4% src/include/catalog/
3.9% src/include/utils/
3.3% src/include/
8.9% src/test/regress/expected/
7.1% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 3f4a27a736e..77b41e02b82 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..12ae9a8d321 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,23 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..d3491faaff2 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Xact statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--SywurcztURg2uk+W--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v3 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. A new function is called in AtEOXact_PgStat() to increment those
two new counters.
---
src/backend/utils/activity/pgstat_backend.c | 57 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
4 files changed, 69 insertions(+), 1 deletion(-)
78.9% src/backend/utils/activity/
11.2% src/include/utils/
9.8% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..bf164854c4b 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,20 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /* Count transaction commit or abort */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..9efc0e10ebf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
--
2.34.1
--u6+Li7FU79Mt/lVd
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v3-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v4 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..47ce61e5093 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -36,7 +36,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index b31f20d41bc..400a8c5d734 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -342,6 +342,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..0fdbaf79780 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -801,6 +809,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..b97184c6539 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,7 +616,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--jg9kqX3osLGO+8A7
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v4-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v5 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 7727fed3bda..42bfb9cd38f 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -37,7 +37,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -48,6 +48,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -261,6 +266,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -285,6 +318,10 @@ pgstat_flush_backend(bool nowait, uint32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -300,6 +337,9 @@ pgstat_flush_backend(bool nowait, uint32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index 933dcb5cae5..218ff5e653c 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -352,6 +352,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 8e3549c3752..5d73c4dfbcf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -523,6 +523,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -535,6 +537,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -844,6 +852,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index eed4c6b359c..8077c65e938 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -707,7 +707,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, uint32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--uN5gGi2rtCNSVbpC
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v5-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v1] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 2 +
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 160 insertions(+), 1 deletion(-)
26.9% doc/src/sgml/
29.1% src/backend/utils/activity/
12.3% src/backend/utils/adt/
9.7% src/include/catalog/
4.0% src/include/utils/
9.2% src/test/regress/expected/
7.3% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index fa78031ccbb..ef89683164f 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..7cec0c83071 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,13 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static int pgStatBackendXactCommit = 0;
+static int pgStatBackendXactRollback = 0;
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +266,33 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += pgStatBackendXactCommit;
+ shbackendent->stats.xact_rollback += pgStatBackendXactRollback;
+
+ pgStatBackendXactCommit = pgStatBackendXactRollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +317,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +336,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +441,22 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ pgStatBackendXactCommit++;
+ else
+ pgStatBackendXactRollback++;
+
+ backend_has_xactstats = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..4c9f6b0dcec 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--ZnbSmJtA6EgjuGCe--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v2] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 166 insertions(+), 1 deletion(-)
26.1% doc/src/sgml/
29.1% src/backend/utils/activity/
11.9% src/backend/utils/adt/
9.4% src/include/catalog/
3.9% src/include/utils/
3.3% src/include/
8.9% src/test/regress/expected/
7.1% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 3f4a27a736e..77b41e02b82 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..12ae9a8d321 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,23 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..d3491faaff2 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Xact statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--SywurcztURg2uk+W--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v3 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. A new function is called in AtEOXact_PgStat() to increment those
two new counters.
---
src/backend/utils/activity/pgstat_backend.c | 57 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
4 files changed, 69 insertions(+), 1 deletion(-)
78.9% src/backend/utils/activity/
11.2% src/include/utils/
9.8% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..bf164854c4b 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,20 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /* Count transaction commit or abort */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..9efc0e10ebf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
--
2.34.1
--u6+Li7FU79Mt/lVd
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v3-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v4 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..47ce61e5093 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -36,7 +36,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index b31f20d41bc..400a8c5d734 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -342,6 +342,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..0fdbaf79780 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -801,6 +809,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..b97184c6539 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,7 +616,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--jg9kqX3osLGO+8A7
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v4-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v5 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 7727fed3bda..42bfb9cd38f 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -37,7 +37,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -48,6 +48,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -261,6 +266,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -285,6 +318,10 @@ pgstat_flush_backend(bool nowait, uint32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -300,6 +337,9 @@ pgstat_flush_backend(bool nowait, uint32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index 933dcb5cae5..218ff5e653c 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -352,6 +352,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 8e3549c3752..5d73c4dfbcf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -523,6 +523,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -535,6 +537,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -844,6 +852,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index eed4c6b359c..8077c65e938 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -707,7 +707,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, uint32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--uN5gGi2rtCNSVbpC
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v5-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v1] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 2 +
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 160 insertions(+), 1 deletion(-)
26.9% doc/src/sgml/
29.1% src/backend/utils/activity/
12.3% src/backend/utils/adt/
9.7% src/include/catalog/
4.0% src/include/utils/
9.2% src/test/regress/expected/
7.3% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index fa78031ccbb..ef89683164f 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..7cec0c83071 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,13 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static int pgStatBackendXactCommit = 0;
+static int pgStatBackendXactRollback = 0;
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +266,33 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += pgStatBackendXactCommit;
+ shbackendent->stats.xact_rollback += pgStatBackendXactRollback;
+
+ pgStatBackendXactCommit = pgStatBackendXactRollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +317,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +336,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +441,22 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ pgStatBackendXactCommit++;
+ else
+ pgStatBackendXactRollback++;
+
+ backend_has_xactstats = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..4c9f6b0dcec 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--ZnbSmJtA6EgjuGCe--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v2] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 166 insertions(+), 1 deletion(-)
26.1% doc/src/sgml/
29.1% src/backend/utils/activity/
11.9% src/backend/utils/adt/
9.4% src/include/catalog/
3.9% src/include/utils/
3.3% src/include/
8.9% src/test/regress/expected/
7.1% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 3f4a27a736e..77b41e02b82 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..12ae9a8d321 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,23 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..d3491faaff2 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Xact statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--SywurcztURg2uk+W--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v3 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. A new function is called in AtEOXact_PgStat() to increment those
two new counters.
---
src/backend/utils/activity/pgstat_backend.c | 57 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
4 files changed, 69 insertions(+), 1 deletion(-)
78.9% src/backend/utils/activity/
11.2% src/include/utils/
9.8% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..bf164854c4b 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,20 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /* Count transaction commit or abort */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..9efc0e10ebf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
--
2.34.1
--u6+Li7FU79Mt/lVd
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v3-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v5 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 7727fed3bda..42bfb9cd38f 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -37,7 +37,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -48,6 +48,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -261,6 +266,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -285,6 +318,10 @@ pgstat_flush_backend(bool nowait, uint32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -300,6 +337,9 @@ pgstat_flush_backend(bool nowait, uint32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index 933dcb5cae5..218ff5e653c 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -352,6 +352,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 8e3549c3752..5d73c4dfbcf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -523,6 +523,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -535,6 +537,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -844,6 +852,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index eed4c6b359c..8077c65e938 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -707,7 +707,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, uint32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--uN5gGi2rtCNSVbpC
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v5-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v1] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 2 +
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 160 insertions(+), 1 deletion(-)
26.9% doc/src/sgml/
29.1% src/backend/utils/activity/
12.3% src/backend/utils/adt/
9.7% src/include/catalog/
4.0% src/include/utils/
9.2% src/test/regress/expected/
7.3% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index fa78031ccbb..ef89683164f 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..7cec0c83071 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,13 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static int pgStatBackendXactCommit = 0;
+static int pgStatBackendXactRollback = 0;
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +266,33 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += pgStatBackendXactCommit;
+ shbackendent->stats.xact_rollback += pgStatBackendXactRollback;
+
+ pgStatBackendXactCommit = pgStatBackendXactRollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +317,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +336,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +441,22 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ pgStatBackendXactCommit++;
+ else
+ pgStatBackendXactRollback++;
+
+ backend_has_xactstats = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..4c9f6b0dcec 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--ZnbSmJtA6EgjuGCe--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v2] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 166 insertions(+), 1 deletion(-)
26.1% doc/src/sgml/
29.1% src/backend/utils/activity/
11.9% src/backend/utils/adt/
9.4% src/include/catalog/
3.9% src/include/utils/
3.3% src/include/
8.9% src/test/regress/expected/
7.1% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 3f4a27a736e..77b41e02b82 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..12ae9a8d321 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,23 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..d3491faaff2 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Xact statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--SywurcztURg2uk+W--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v3 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. A new function is called in AtEOXact_PgStat() to increment those
two new counters.
---
src/backend/utils/activity/pgstat_backend.c | 57 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
4 files changed, 69 insertions(+), 1 deletion(-)
78.9% src/backend/utils/activity/
11.2% src/include/utils/
9.8% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..bf164854c4b 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,20 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /* Count transaction commit or abort */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..9efc0e10ebf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
--
2.34.1
--u6+Li7FU79Mt/lVd
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v3-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v1] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 2 +
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 160 insertions(+), 1 deletion(-)
26.9% doc/src/sgml/
29.1% src/backend/utils/activity/
12.3% src/backend/utils/adt/
9.7% src/include/catalog/
4.0% src/include/utils/
9.2% src/test/regress/expected/
7.3% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index fa78031ccbb..ef89683164f 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..7cec0c83071 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,13 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static int pgStatBackendXactCommit = 0;
+static int pgStatBackendXactRollback = 0;
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +266,33 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += pgStatBackendXactCommit;
+ shbackendent->stats.xact_rollback += pgStatBackendXactRollback;
+
+ pgStatBackendXactCommit = pgStatBackendXactRollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +317,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +336,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +441,22 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ pgStatBackendXactCommit++;
+ else
+ pgStatBackendXactRollback++;
+
+ backend_has_xactstats = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..4c9f6b0dcec 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--ZnbSmJtA6EgjuGCe--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v2] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 166 insertions(+), 1 deletion(-)
26.1% doc/src/sgml/
29.1% src/backend/utils/activity/
11.9% src/backend/utils/adt/
9.4% src/include/catalog/
3.9% src/include/utils/
3.3% src/include/
8.9% src/test/regress/expected/
7.1% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 3f4a27a736e..77b41e02b82 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..12ae9a8d321 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,23 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..d3491faaff2 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Xact statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--SywurcztURg2uk+W--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v3 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. A new function is called in AtEOXact_PgStat() to increment those
two new counters.
---
src/backend/utils/activity/pgstat_backend.c | 57 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
4 files changed, 69 insertions(+), 1 deletion(-)
78.9% src/backend/utils/activity/
11.2% src/include/utils/
9.8% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..bf164854c4b 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,20 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /* Count transaction commit or abort */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..9efc0e10ebf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
--
2.34.1
--u6+Li7FU79Mt/lVd
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v3-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v4 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..47ce61e5093 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -36,7 +36,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index b31f20d41bc..400a8c5d734 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -342,6 +342,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..0fdbaf79780 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -801,6 +809,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..b97184c6539 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,7 +616,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--jg9kqX3osLGO+8A7
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v4-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v5 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 7727fed3bda..42bfb9cd38f 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -37,7 +37,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -48,6 +48,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -261,6 +266,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -285,6 +318,10 @@ pgstat_flush_backend(bool nowait, uint32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -300,6 +337,9 @@ pgstat_flush_backend(bool nowait, uint32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index 933dcb5cae5..218ff5e653c 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -352,6 +352,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 8e3549c3752..5d73c4dfbcf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -523,6 +523,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -535,6 +537,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -844,6 +852,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index eed4c6b359c..8077c65e938 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -707,7 +707,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, uint32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--uN5gGi2rtCNSVbpC
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v5-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v1] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 2 +
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 160 insertions(+), 1 deletion(-)
26.9% doc/src/sgml/
29.1% src/backend/utils/activity/
12.3% src/backend/utils/adt/
9.7% src/include/catalog/
4.0% src/include/utils/
9.2% src/test/regress/expected/
7.3% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index fa78031ccbb..ef89683164f 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..7cec0c83071 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,13 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static int pgStatBackendXactCommit = 0;
+static int pgStatBackendXactRollback = 0;
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +266,33 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += pgStatBackendXactCommit;
+ shbackendent->stats.xact_rollback += pgStatBackendXactRollback;
+
+ pgStatBackendXactCommit = pgStatBackendXactRollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +317,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +336,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +441,22 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ pgStatBackendXactCommit++;
+ else
+ pgStatBackendXactRollback++;
+
+ backend_has_xactstats = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..4c9f6b0dcec 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--ZnbSmJtA6EgjuGCe--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v2] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 166 insertions(+), 1 deletion(-)
26.1% doc/src/sgml/
29.1% src/backend/utils/activity/
11.9% src/backend/utils/adt/
9.4% src/include/catalog/
3.9% src/include/utils/
3.3% src/include/
8.9% src/test/regress/expected/
7.1% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 3f4a27a736e..77b41e02b82 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..12ae9a8d321 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,23 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..d3491faaff2 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Xact statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--SywurcztURg2uk+W--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v3 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. A new function is called in AtEOXact_PgStat() to increment those
two new counters.
---
src/backend/utils/activity/pgstat_backend.c | 57 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
4 files changed, 69 insertions(+), 1 deletion(-)
78.9% src/backend/utils/activity/
11.2% src/include/utils/
9.8% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..bf164854c4b 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,20 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /* Count transaction commit or abort */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..9efc0e10ebf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
--
2.34.1
--u6+Li7FU79Mt/lVd
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v3-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v4 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..47ce61e5093 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -36,7 +36,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index b31f20d41bc..400a8c5d734 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -342,6 +342,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..0fdbaf79780 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -801,6 +809,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..b97184c6539 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,7 +616,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--jg9kqX3osLGO+8A7
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v4-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v5 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 7727fed3bda..42bfb9cd38f 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -37,7 +37,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -48,6 +48,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -261,6 +266,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -285,6 +318,10 @@ pgstat_flush_backend(bool nowait, uint32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -300,6 +337,9 @@ pgstat_flush_backend(bool nowait, uint32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index 933dcb5cae5..218ff5e653c 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -352,6 +352,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 8e3549c3752..5d73c4dfbcf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -523,6 +523,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -535,6 +537,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -844,6 +852,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index eed4c6b359c..8077c65e938 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -707,7 +707,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, uint32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--uN5gGi2rtCNSVbpC
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v5-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v4 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..47ce61e5093 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -36,7 +36,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index b31f20d41bc..400a8c5d734 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -342,6 +342,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..0fdbaf79780 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -801,6 +809,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..b97184c6539 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,7 +616,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--jg9kqX3osLGO+8A7
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v4-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v5 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 7727fed3bda..42bfb9cd38f 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -37,7 +37,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -48,6 +48,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -261,6 +266,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -285,6 +318,10 @@ pgstat_flush_backend(bool nowait, uint32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -300,6 +337,9 @@ pgstat_flush_backend(bool nowait, uint32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index 933dcb5cae5..218ff5e653c 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -352,6 +352,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 8e3549c3752..5d73c4dfbcf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -523,6 +523,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -535,6 +537,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -844,6 +852,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index eed4c6b359c..8077c65e938 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -707,7 +707,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, uint32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--uN5gGi2rtCNSVbpC
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v5-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v1] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 2 +
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 160 insertions(+), 1 deletion(-)
26.9% doc/src/sgml/
29.1% src/backend/utils/activity/
12.3% src/backend/utils/adt/
9.7% src/include/catalog/
4.0% src/include/utils/
9.2% src/test/regress/expected/
7.3% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index fa78031ccbb..ef89683164f 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..7cec0c83071 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,13 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static int pgStatBackendXactCommit = 0;
+static int pgStatBackendXactRollback = 0;
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +266,33 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += pgStatBackendXactCommit;
+ shbackendent->stats.xact_rollback += pgStatBackendXactRollback;
+
+ pgStatBackendXactCommit = pgStatBackendXactRollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +317,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +336,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +441,22 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ pgStatBackendXactCommit++;
+ else
+ pgStatBackendXactRollback++;
+
+ backend_has_xactstats = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..4c9f6b0dcec 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--ZnbSmJtA6EgjuGCe--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v2] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 166 insertions(+), 1 deletion(-)
26.1% doc/src/sgml/
29.1% src/backend/utils/activity/
11.9% src/backend/utils/adt/
9.4% src/include/catalog/
3.9% src/include/utils/
3.3% src/include/
8.9% src/test/regress/expected/
7.1% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 3f4a27a736e..77b41e02b82 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..12ae9a8d321 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,23 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..d3491faaff2 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Xact statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--SywurcztURg2uk+W--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v3 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. A new function is called in AtEOXact_PgStat() to increment those
two new counters.
---
src/backend/utils/activity/pgstat_backend.c | 57 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
4 files changed, 69 insertions(+), 1 deletion(-)
78.9% src/backend/utils/activity/
11.2% src/include/utils/
9.8% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..bf164854c4b 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,20 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /* Count transaction commit or abort */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..9efc0e10ebf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
--
2.34.1
--u6+Li7FU79Mt/lVd
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v3-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v5 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 7727fed3bda..42bfb9cd38f 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -37,7 +37,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -48,6 +48,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -261,6 +266,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -285,6 +318,10 @@ pgstat_flush_backend(bool nowait, uint32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -300,6 +337,9 @@ pgstat_flush_backend(bool nowait, uint32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index 933dcb5cae5..218ff5e653c 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -352,6 +352,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 8e3549c3752..5d73c4dfbcf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -523,6 +523,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -535,6 +537,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -844,6 +852,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index eed4c6b359c..8077c65e938 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -707,7 +707,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, uint32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--uN5gGi2rtCNSVbpC
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v5-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v1] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 2 +
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 160 insertions(+), 1 deletion(-)
26.9% doc/src/sgml/
29.1% src/backend/utils/activity/
12.3% src/backend/utils/adt/
9.7% src/include/catalog/
4.0% src/include/utils/
9.2% src/test/regress/expected/
7.3% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index fa78031ccbb..ef89683164f 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..7cec0c83071 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,13 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static int pgStatBackendXactCommit = 0;
+static int pgStatBackendXactRollback = 0;
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +266,33 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += pgStatBackendXactCommit;
+ shbackendent->stats.xact_rollback += pgStatBackendXactRollback;
+
+ pgStatBackendXactCommit = pgStatBackendXactRollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +317,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +336,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +441,22 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ pgStatBackendXactCommit++;
+ else
+ pgStatBackendXactRollback++;
+
+ backend_has_xactstats = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..4c9f6b0dcec 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--ZnbSmJtA6EgjuGCe--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v2] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 166 insertions(+), 1 deletion(-)
26.1% doc/src/sgml/
29.1% src/backend/utils/activity/
11.9% src/backend/utils/adt/
9.4% src/include/catalog/
3.9% src/include/utils/
3.3% src/include/
8.9% src/test/regress/expected/
7.1% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 3f4a27a736e..77b41e02b82 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..12ae9a8d321 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,23 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..d3491faaff2 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Xact statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--SywurcztURg2uk+W--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v3 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. A new function is called in AtEOXact_PgStat() to increment those
two new counters.
---
src/backend/utils/activity/pgstat_backend.c | 57 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
4 files changed, 69 insertions(+), 1 deletion(-)
78.9% src/backend/utils/activity/
11.2% src/include/utils/
9.8% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..bf164854c4b 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,20 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /* Count transaction commit or abort */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..9efc0e10ebf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
--
2.34.1
--u6+Li7FU79Mt/lVd
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v3-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v4 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..47ce61e5093 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -36,7 +36,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index b31f20d41bc..400a8c5d734 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -342,6 +342,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..0fdbaf79780 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -801,6 +809,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..b97184c6539 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,7 +616,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--jg9kqX3osLGO+8A7
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v4-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v5 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 7727fed3bda..42bfb9cd38f 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -37,7 +37,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -48,6 +48,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -261,6 +266,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -285,6 +318,10 @@ pgstat_flush_backend(bool nowait, uint32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -300,6 +337,9 @@ pgstat_flush_backend(bool nowait, uint32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index 933dcb5cae5..218ff5e653c 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -352,6 +352,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 8e3549c3752..5d73c4dfbcf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -523,6 +523,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -535,6 +537,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -844,6 +852,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index eed4c6b359c..8077c65e938 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -707,7 +707,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, uint32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--uN5gGi2rtCNSVbpC
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v5-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v4 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..47ce61e5093 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -36,7 +36,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index b31f20d41bc..400a8c5d734 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -342,6 +342,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..0fdbaf79780 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -801,6 +809,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..b97184c6539 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,7 +616,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--jg9kqX3osLGO+8A7
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v4-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v1] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 2 +
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 160 insertions(+), 1 deletion(-)
26.9% doc/src/sgml/
29.1% src/backend/utils/activity/
12.3% src/backend/utils/adt/
9.7% src/include/catalog/
4.0% src/include/utils/
9.2% src/test/regress/expected/
7.3% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index fa78031ccbb..ef89683164f 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..7cec0c83071 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,13 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static int pgStatBackendXactCommit = 0;
+static int pgStatBackendXactRollback = 0;
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +266,33 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += pgStatBackendXactCommit;
+ shbackendent->stats.xact_rollback += pgStatBackendXactRollback;
+
+ pgStatBackendXactCommit = pgStatBackendXactRollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +317,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +336,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +441,22 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ pgStatBackendXactCommit++;
+ else
+ pgStatBackendXactRollback++;
+
+ backend_has_xactstats = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..4c9f6b0dcec 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--ZnbSmJtA6EgjuGCe--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v2] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 166 insertions(+), 1 deletion(-)
26.1% doc/src/sgml/
29.1% src/backend/utils/activity/
11.9% src/backend/utils/adt/
9.4% src/include/catalog/
3.9% src/include/utils/
3.3% src/include/
8.9% src/test/regress/expected/
7.1% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 3f4a27a736e..77b41e02b82 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..12ae9a8d321 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,23 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..d3491faaff2 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Xact statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--SywurcztURg2uk+W--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v3 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. A new function is called in AtEOXact_PgStat() to increment those
two new counters.
---
src/backend/utils/activity/pgstat_backend.c | 57 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
4 files changed, 69 insertions(+), 1 deletion(-)
78.9% src/backend/utils/activity/
11.2% src/include/utils/
9.8% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..bf164854c4b 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,20 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /* Count transaction commit or abort */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..9efc0e10ebf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
--
2.34.1
--u6+Li7FU79Mt/lVd
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v3-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v4 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..47ce61e5093 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -36,7 +36,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index b31f20d41bc..400a8c5d734 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -342,6 +342,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..0fdbaf79780 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -801,6 +809,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..b97184c6539 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,7 +616,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--jg9kqX3osLGO+8A7
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v4-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v5 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 7727fed3bda..42bfb9cd38f 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -37,7 +37,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -48,6 +48,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -261,6 +266,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -285,6 +318,10 @@ pgstat_flush_backend(bool nowait, uint32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -300,6 +337,9 @@ pgstat_flush_backend(bool nowait, uint32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index 933dcb5cae5..218ff5e653c 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -352,6 +352,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 8e3549c3752..5d73c4dfbcf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -523,6 +523,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -535,6 +537,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -844,6 +852,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index eed4c6b359c..8077c65e938 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -707,7 +707,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, uint32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--uN5gGi2rtCNSVbpC
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v5-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v1] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 2 +
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 160 insertions(+), 1 deletion(-)
26.9% doc/src/sgml/
29.1% src/backend/utils/activity/
12.3% src/backend/utils/adt/
9.7% src/include/catalog/
4.0% src/include/utils/
9.2% src/test/regress/expected/
7.3% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index fa78031ccbb..ef89683164f 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..7cec0c83071 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,13 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static int pgStatBackendXactCommit = 0;
+static int pgStatBackendXactRollback = 0;
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +266,33 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += pgStatBackendXactCommit;
+ shbackendent->stats.xact_rollback += pgStatBackendXactRollback;
+
+ pgStatBackendXactCommit = pgStatBackendXactRollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +317,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +336,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +441,22 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ pgStatBackendXactCommit++;
+ else
+ pgStatBackendXactRollback++;
+
+ backend_has_xactstats = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..4c9f6b0dcec 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--ZnbSmJtA6EgjuGCe--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v2] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 166 insertions(+), 1 deletion(-)
26.1% doc/src/sgml/
29.1% src/backend/utils/activity/
11.9% src/backend/utils/adt/
9.4% src/include/catalog/
3.9% src/include/utils/
3.3% src/include/
8.9% src/test/regress/expected/
7.1% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 3f4a27a736e..77b41e02b82 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..12ae9a8d321 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,23 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..d3491faaff2 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Xact statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--SywurcztURg2uk+W--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v3 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. A new function is called in AtEOXact_PgStat() to increment those
two new counters.
---
src/backend/utils/activity/pgstat_backend.c | 57 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
4 files changed, 69 insertions(+), 1 deletion(-)
78.9% src/backend/utils/activity/
11.2% src/include/utils/
9.8% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..bf164854c4b 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,20 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /* Count transaction commit or abort */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..9efc0e10ebf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
--
2.34.1
--u6+Li7FU79Mt/lVd
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v3-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v4 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..47ce61e5093 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -36,7 +36,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index b31f20d41bc..400a8c5d734 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -342,6 +342,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..0fdbaf79780 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -801,6 +809,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..b97184c6539 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,7 +616,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--jg9kqX3osLGO+8A7
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v4-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v5 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 7727fed3bda..42bfb9cd38f 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -37,7 +37,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -48,6 +48,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -261,6 +266,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -285,6 +318,10 @@ pgstat_flush_backend(bool nowait, uint32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -300,6 +337,9 @@ pgstat_flush_backend(bool nowait, uint32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index 933dcb5cae5..218ff5e653c 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -352,6 +352,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 8e3549c3752..5d73c4dfbcf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -523,6 +523,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -535,6 +537,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -844,6 +852,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index eed4c6b359c..8077c65e938 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -707,7 +707,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, uint32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--uN5gGi2rtCNSVbpC
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v5-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v1] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 2 +
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 160 insertions(+), 1 deletion(-)
26.9% doc/src/sgml/
29.1% src/backend/utils/activity/
12.3% src/backend/utils/adt/
9.7% src/include/catalog/
4.0% src/include/utils/
9.2% src/test/regress/expected/
7.3% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index fa78031ccbb..ef89683164f 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..7cec0c83071 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,13 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static int pgStatBackendXactCommit = 0;
+static int pgStatBackendXactRollback = 0;
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +266,33 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += pgStatBackendXactCommit;
+ shbackendent->stats.xact_rollback += pgStatBackendXactRollback;
+
+ pgStatBackendXactCommit = pgStatBackendXactRollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +317,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +336,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +441,22 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ pgStatBackendXactCommit++;
+ else
+ pgStatBackendXactRollback++;
+
+ backend_has_xactstats = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..4c9f6b0dcec 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--ZnbSmJtA6EgjuGCe--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v2] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 166 insertions(+), 1 deletion(-)
26.1% doc/src/sgml/
29.1% src/backend/utils/activity/
11.9% src/backend/utils/adt/
9.4% src/include/catalog/
3.9% src/include/utils/
3.3% src/include/
8.9% src/test/regress/expected/
7.1% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 3f4a27a736e..77b41e02b82 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..12ae9a8d321 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,23 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..d3491faaff2 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Xact statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--SywurcztURg2uk+W--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v3 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. A new function is called in AtEOXact_PgStat() to increment those
two new counters.
---
src/backend/utils/activity/pgstat_backend.c | 57 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
4 files changed, 69 insertions(+), 1 deletion(-)
78.9% src/backend/utils/activity/
11.2% src/include/utils/
9.8% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..bf164854c4b 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,20 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /* Count transaction commit or abort */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..9efc0e10ebf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
--
2.34.1
--u6+Li7FU79Mt/lVd
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v3-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v4 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..47ce61e5093 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -36,7 +36,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index b31f20d41bc..400a8c5d734 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -342,6 +342,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..0fdbaf79780 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -801,6 +809,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..b97184c6539 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,7 +616,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--jg9kqX3osLGO+8A7
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v4-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v5 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 7727fed3bda..42bfb9cd38f 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -37,7 +37,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -48,6 +48,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -261,6 +266,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -285,6 +318,10 @@ pgstat_flush_backend(bool nowait, uint32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -300,6 +337,9 @@ pgstat_flush_backend(bool nowait, uint32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index 933dcb5cae5..218ff5e653c 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -352,6 +352,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 8e3549c3752..5d73c4dfbcf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -523,6 +523,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -535,6 +537,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -844,6 +852,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index eed4c6b359c..8077c65e938 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -707,7 +707,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, uint32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--uN5gGi2rtCNSVbpC
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v5-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v1] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 2 +
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 160 insertions(+), 1 deletion(-)
26.9% doc/src/sgml/
29.1% src/backend/utils/activity/
12.3% src/backend/utils/adt/
9.7% src/include/catalog/
4.0% src/include/utils/
9.2% src/test/regress/expected/
7.3% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index fa78031ccbb..ef89683164f 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..7cec0c83071 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,13 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static int pgStatBackendXactCommit = 0;
+static int pgStatBackendXactRollback = 0;
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +266,33 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += pgStatBackendXactCommit;
+ shbackendent->stats.xact_rollback += pgStatBackendXactRollback;
+
+ pgStatBackendXactCommit = pgStatBackendXactRollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +317,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +336,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +441,22 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ pgStatBackendXactCommit++;
+ else
+ pgStatBackendXactRollback++;
+
+ backend_has_xactstats = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..4c9f6b0dcec 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--ZnbSmJtA6EgjuGCe--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v2] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 166 insertions(+), 1 deletion(-)
26.1% doc/src/sgml/
29.1% src/backend/utils/activity/
11.9% src/backend/utils/adt/
9.4% src/include/catalog/
3.9% src/include/utils/
3.3% src/include/
8.9% src/test/regress/expected/
7.1% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 3f4a27a736e..77b41e02b82 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..12ae9a8d321 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,23 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..d3491faaff2 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Xact statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--SywurcztURg2uk+W--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v3 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. A new function is called in AtEOXact_PgStat() to increment those
two new counters.
---
src/backend/utils/activity/pgstat_backend.c | 57 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
4 files changed, 69 insertions(+), 1 deletion(-)
78.9% src/backend/utils/activity/
11.2% src/include/utils/
9.8% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..bf164854c4b 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,20 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /* Count transaction commit or abort */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..9efc0e10ebf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
--
2.34.1
--u6+Li7FU79Mt/lVd
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v3-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v4 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..47ce61e5093 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -36,7 +36,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index b31f20d41bc..400a8c5d734 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -342,6 +342,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..0fdbaf79780 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -801,6 +809,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..b97184c6539 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,7 +616,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--jg9kqX3osLGO+8A7
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v4-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v5 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 7727fed3bda..42bfb9cd38f 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -37,7 +37,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -48,6 +48,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -261,6 +266,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -285,6 +318,10 @@ pgstat_flush_backend(bool nowait, uint32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -300,6 +337,9 @@ pgstat_flush_backend(bool nowait, uint32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index 933dcb5cae5..218ff5e653c 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -352,6 +352,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 8e3549c3752..5d73c4dfbcf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -523,6 +523,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -535,6 +537,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -844,6 +852,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index eed4c6b359c..8077c65e938 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -707,7 +707,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, uint32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--uN5gGi2rtCNSVbpC
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v5-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v1] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 2 +
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 160 insertions(+), 1 deletion(-)
26.9% doc/src/sgml/
29.1% src/backend/utils/activity/
12.3% src/backend/utils/adt/
9.7% src/include/catalog/
4.0% src/include/utils/
9.2% src/test/regress/expected/
7.3% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index fa78031ccbb..ef89683164f 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..7cec0c83071 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,13 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static int pgStatBackendXactCommit = 0;
+static int pgStatBackendXactRollback = 0;
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +266,33 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += pgStatBackendXactCommit;
+ shbackendent->stats.xact_rollback += pgStatBackendXactRollback;
+
+ pgStatBackendXactCommit = pgStatBackendXactRollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +317,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +336,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +441,22 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ pgStatBackendXactCommit++;
+ else
+ pgStatBackendXactRollback++;
+
+ backend_has_xactstats = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..4c9f6b0dcec 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--ZnbSmJtA6EgjuGCe--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v2] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 166 insertions(+), 1 deletion(-)
26.1% doc/src/sgml/
29.1% src/backend/utils/activity/
11.9% src/backend/utils/adt/
9.4% src/include/catalog/
3.9% src/include/utils/
3.3% src/include/
8.9% src/test/regress/expected/
7.1% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 3f4a27a736e..77b41e02b82 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..12ae9a8d321 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,23 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..d3491faaff2 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Xact statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--SywurcztURg2uk+W--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v3 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. A new function is called in AtEOXact_PgStat() to increment those
two new counters.
---
src/backend/utils/activity/pgstat_backend.c | 57 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
4 files changed, 69 insertions(+), 1 deletion(-)
78.9% src/backend/utils/activity/
11.2% src/include/utils/
9.8% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..bf164854c4b 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,20 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /* Count transaction commit or abort */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..9efc0e10ebf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
--
2.34.1
--u6+Li7FU79Mt/lVd
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v3-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v4 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..47ce61e5093 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -36,7 +36,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index b31f20d41bc..400a8c5d734 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -342,6 +342,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..0fdbaf79780 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -801,6 +809,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..b97184c6539 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,7 +616,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--jg9kqX3osLGO+8A7
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v4-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v5 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 7727fed3bda..42bfb9cd38f 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -37,7 +37,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -48,6 +48,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -261,6 +266,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -285,6 +318,10 @@ pgstat_flush_backend(bool nowait, uint32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -300,6 +337,9 @@ pgstat_flush_backend(bool nowait, uint32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index 933dcb5cae5..218ff5e653c 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -352,6 +352,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 8e3549c3752..5d73c4dfbcf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -523,6 +523,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -535,6 +537,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -844,6 +852,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index eed4c6b359c..8077c65e938 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -707,7 +707,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, uint32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--uN5gGi2rtCNSVbpC
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v5-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v4 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..47ce61e5093 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -36,7 +36,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index b31f20d41bc..400a8c5d734 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -342,6 +342,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..0fdbaf79780 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -801,6 +809,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..b97184c6539 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,7 +616,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--jg9kqX3osLGO+8A7
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v4-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v5 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 7727fed3bda..42bfb9cd38f 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -37,7 +37,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -48,6 +48,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -261,6 +266,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -285,6 +318,10 @@ pgstat_flush_backend(bool nowait, uint32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -300,6 +337,9 @@ pgstat_flush_backend(bool nowait, uint32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index 933dcb5cae5..218ff5e653c 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -352,6 +352,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 8e3549c3752..5d73c4dfbcf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -523,6 +523,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -535,6 +537,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -844,6 +852,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index eed4c6b359c..8077c65e938 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -707,7 +707,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, uint32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--uN5gGi2rtCNSVbpC
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v5-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v1] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 2 +
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 160 insertions(+), 1 deletion(-)
26.9% doc/src/sgml/
29.1% src/backend/utils/activity/
12.3% src/backend/utils/adt/
9.7% src/include/catalog/
4.0% src/include/utils/
9.2% src/test/regress/expected/
7.3% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index fa78031ccbb..ef89683164f 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..7cec0c83071 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,13 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static int pgStatBackendXactCommit = 0;
+static int pgStatBackendXactRollback = 0;
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +266,33 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += pgStatBackendXactCommit;
+ shbackendent->stats.xact_rollback += pgStatBackendXactRollback;
+
+ pgStatBackendXactCommit = pgStatBackendXactRollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +317,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +336,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +441,22 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ pgStatBackendXactCommit++;
+ else
+ pgStatBackendXactRollback++;
+
+ backend_has_xactstats = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..4c9f6b0dcec 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--ZnbSmJtA6EgjuGCe--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v2] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 166 insertions(+), 1 deletion(-)
26.1% doc/src/sgml/
29.1% src/backend/utils/activity/
11.9% src/backend/utils/adt/
9.4% src/include/catalog/
3.9% src/include/utils/
3.3% src/include/
8.9% src/test/regress/expected/
7.1% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 3f4a27a736e..77b41e02b82 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..12ae9a8d321 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,23 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..d3491faaff2 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Xact statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--SywurcztURg2uk+W--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v3 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. A new function is called in AtEOXact_PgStat() to increment those
two new counters.
---
src/backend/utils/activity/pgstat_backend.c | 57 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
4 files changed, 69 insertions(+), 1 deletion(-)
78.9% src/backend/utils/activity/
11.2% src/include/utils/
9.8% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..bf164854c4b 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,20 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /* Count transaction commit or abort */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..9efc0e10ebf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
--
2.34.1
--u6+Li7FU79Mt/lVd
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v3-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v4 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..47ce61e5093 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -36,7 +36,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index b31f20d41bc..400a8c5d734 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -342,6 +342,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..0fdbaf79780 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -801,6 +809,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..b97184c6539 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,7 +616,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--jg9kqX3osLGO+8A7
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v4-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v5 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 7727fed3bda..42bfb9cd38f 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -37,7 +37,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -48,6 +48,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -261,6 +266,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -285,6 +318,10 @@ pgstat_flush_backend(bool nowait, uint32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -300,6 +337,9 @@ pgstat_flush_backend(bool nowait, uint32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index 933dcb5cae5..218ff5e653c 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -352,6 +352,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 8e3549c3752..5d73c4dfbcf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -523,6 +523,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -535,6 +537,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -844,6 +852,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index eed4c6b359c..8077c65e938 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -707,7 +707,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, uint32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--uN5gGi2rtCNSVbpC
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v5-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v5 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 7727fed3bda..42bfb9cd38f 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -37,7 +37,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -48,6 +48,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -261,6 +266,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -285,6 +318,10 @@ pgstat_flush_backend(bool nowait, uint32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -300,6 +337,9 @@ pgstat_flush_backend(bool nowait, uint32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index 933dcb5cae5..218ff5e653c 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -352,6 +352,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 8e3549c3752..5d73c4dfbcf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -523,6 +523,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -535,6 +537,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -844,6 +852,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index eed4c6b359c..8077c65e938 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -707,7 +707,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, uint32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--uN5gGi2rtCNSVbpC
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v5-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v1] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 2 +
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 160 insertions(+), 1 deletion(-)
26.9% doc/src/sgml/
29.1% src/backend/utils/activity/
12.3% src/backend/utils/adt/
9.7% src/include/catalog/
4.0% src/include/utils/
9.2% src/test/regress/expected/
7.3% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index fa78031ccbb..ef89683164f 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..7cec0c83071 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,13 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static int pgStatBackendXactCommit = 0;
+static int pgStatBackendXactRollback = 0;
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +266,33 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += pgStatBackendXactCommit;
+ shbackendent->stats.xact_rollback += pgStatBackendXactRollback;
+
+ pgStatBackendXactCommit = pgStatBackendXactRollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +317,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +336,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +441,22 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ pgStatBackendXactCommit++;
+ else
+ pgStatBackendXactRollback++;
+
+ backend_has_xactstats = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..4c9f6b0dcec 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--ZnbSmJtA6EgjuGCe--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v2] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 166 insertions(+), 1 deletion(-)
26.1% doc/src/sgml/
29.1% src/backend/utils/activity/
11.9% src/backend/utils/adt/
9.4% src/include/catalog/
3.9% src/include/utils/
3.3% src/include/
8.9% src/test/regress/expected/
7.1% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 3f4a27a736e..77b41e02b82 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..12ae9a8d321 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,23 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..d3491faaff2 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Xact statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--SywurcztURg2uk+W--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v3 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. A new function is called in AtEOXact_PgStat() to increment those
two new counters.
---
src/backend/utils/activity/pgstat_backend.c | 57 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
4 files changed, 69 insertions(+), 1 deletion(-)
78.9% src/backend/utils/activity/
11.2% src/include/utils/
9.8% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..bf164854c4b 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,20 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /* Count transaction commit or abort */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..9efc0e10ebf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
--
2.34.1
--u6+Li7FU79Mt/lVd
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v3-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v4 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..47ce61e5093 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -36,7 +36,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index b31f20d41bc..400a8c5d734 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -342,6 +342,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..0fdbaf79780 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -801,6 +809,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..b97184c6539 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,7 +616,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--jg9kqX3osLGO+8A7
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v4-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v5 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 7727fed3bda..42bfb9cd38f 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -37,7 +37,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -48,6 +48,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -261,6 +266,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -285,6 +318,10 @@ pgstat_flush_backend(bool nowait, uint32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -300,6 +337,9 @@ pgstat_flush_backend(bool nowait, uint32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index 933dcb5cae5..218ff5e653c 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -352,6 +352,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 8e3549c3752..5d73c4dfbcf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -523,6 +523,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -535,6 +537,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -844,6 +852,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index eed4c6b359c..8077c65e938 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -707,7 +707,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, uint32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--uN5gGi2rtCNSVbpC
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v5-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v1] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 2 +
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 160 insertions(+), 1 deletion(-)
26.9% doc/src/sgml/
29.1% src/backend/utils/activity/
12.3% src/backend/utils/adt/
9.7% src/include/catalog/
4.0% src/include/utils/
9.2% src/test/regress/expected/
7.3% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index fa78031ccbb..ef89683164f 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..7cec0c83071 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,13 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static int pgStatBackendXactCommit = 0;
+static int pgStatBackendXactRollback = 0;
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +266,33 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += pgStatBackendXactCommit;
+ shbackendent->stats.xact_rollback += pgStatBackendXactRollback;
+
+ pgStatBackendXactCommit = pgStatBackendXactRollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +317,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +336,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +441,22 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ pgStatBackendXactCommit++;
+ else
+ pgStatBackendXactRollback++;
+
+ backend_has_xactstats = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..4c9f6b0dcec 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--ZnbSmJtA6EgjuGCe--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v2] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 166 insertions(+), 1 deletion(-)
26.1% doc/src/sgml/
29.1% src/backend/utils/activity/
11.9% src/backend/utils/adt/
9.4% src/include/catalog/
3.9% src/include/utils/
3.3% src/include/
8.9% src/test/regress/expected/
7.1% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 3f4a27a736e..77b41e02b82 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..12ae9a8d321 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,23 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..d3491faaff2 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Xact statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--SywurcztURg2uk+W--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v3 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. A new function is called in AtEOXact_PgStat() to increment those
two new counters.
---
src/backend/utils/activity/pgstat_backend.c | 57 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
4 files changed, 69 insertions(+), 1 deletion(-)
78.9% src/backend/utils/activity/
11.2% src/include/utils/
9.8% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..bf164854c4b 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,20 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /* Count transaction commit or abort */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..9efc0e10ebf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
--
2.34.1
--u6+Li7FU79Mt/lVd
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v3-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v4 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..47ce61e5093 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -36,7 +36,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index b31f20d41bc..400a8c5d734 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -342,6 +342,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..0fdbaf79780 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -801,6 +809,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..b97184c6539 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,7 +616,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--jg9kqX3osLGO+8A7
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v4-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v5 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 7727fed3bda..42bfb9cd38f 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -37,7 +37,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -48,6 +48,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -261,6 +266,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -285,6 +318,10 @@ pgstat_flush_backend(bool nowait, uint32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -300,6 +337,9 @@ pgstat_flush_backend(bool nowait, uint32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index 933dcb5cae5..218ff5e653c 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -352,6 +352,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 8e3549c3752..5d73c4dfbcf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -523,6 +523,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -535,6 +537,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -844,6 +852,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index eed4c6b359c..8077c65e938 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -707,7 +707,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, uint32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--uN5gGi2rtCNSVbpC
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v5-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v1] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 2 +
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 160 insertions(+), 1 deletion(-)
26.9% doc/src/sgml/
29.1% src/backend/utils/activity/
12.3% src/backend/utils/adt/
9.7% src/include/catalog/
4.0% src/include/utils/
9.2% src/test/regress/expected/
7.3% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index fa78031ccbb..ef89683164f 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..7cec0c83071 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,13 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static int pgStatBackendXactCommit = 0;
+static int pgStatBackendXactRollback = 0;
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +266,33 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += pgStatBackendXactCommit;
+ shbackendent->stats.xact_rollback += pgStatBackendXactRollback;
+
+ pgStatBackendXactCommit = pgStatBackendXactRollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +317,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +336,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +441,22 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ pgStatBackendXactCommit++;
+ else
+ pgStatBackendXactRollback++;
+
+ backend_has_xactstats = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..4c9f6b0dcec 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--ZnbSmJtA6EgjuGCe--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v2] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 166 insertions(+), 1 deletion(-)
26.1% doc/src/sgml/
29.1% src/backend/utils/activity/
11.9% src/backend/utils/adt/
9.4% src/include/catalog/
3.9% src/include/utils/
3.3% src/include/
8.9% src/test/regress/expected/
7.1% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 3f4a27a736e..77b41e02b82 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..12ae9a8d321 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,23 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..d3491faaff2 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Xact statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--SywurcztURg2uk+W--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v3 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. A new function is called in AtEOXact_PgStat() to increment those
two new counters.
---
src/backend/utils/activity/pgstat_backend.c | 57 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
4 files changed, 69 insertions(+), 1 deletion(-)
78.9% src/backend/utils/activity/
11.2% src/include/utils/
9.8% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..bf164854c4b 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,20 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /* Count transaction commit or abort */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..9efc0e10ebf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
--
2.34.1
--u6+Li7FU79Mt/lVd
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v3-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v4 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..47ce61e5093 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -36,7 +36,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index b31f20d41bc..400a8c5d734 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -342,6 +342,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..0fdbaf79780 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -801,6 +809,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..b97184c6539 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,7 +616,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--jg9kqX3osLGO+8A7
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v4-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v5 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 7727fed3bda..42bfb9cd38f 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -37,7 +37,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -48,6 +48,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -261,6 +266,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -285,6 +318,10 @@ pgstat_flush_backend(bool nowait, uint32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -300,6 +337,9 @@ pgstat_flush_backend(bool nowait, uint32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index 933dcb5cae5..218ff5e653c 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -352,6 +352,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 8e3549c3752..5d73c4dfbcf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -523,6 +523,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -535,6 +537,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -844,6 +852,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index eed4c6b359c..8077c65e938 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -707,7 +707,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, uint32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--uN5gGi2rtCNSVbpC
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v5-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v1] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 2 +
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 160 insertions(+), 1 deletion(-)
26.9% doc/src/sgml/
29.1% src/backend/utils/activity/
12.3% src/backend/utils/adt/
9.7% src/include/catalog/
4.0% src/include/utils/
9.2% src/test/regress/expected/
7.3% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index fa78031ccbb..ef89683164f 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..7cec0c83071 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,13 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static int pgStatBackendXactCommit = 0;
+static int pgStatBackendXactRollback = 0;
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +266,33 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += pgStatBackendXactCommit;
+ shbackendent->stats.xact_rollback += pgStatBackendXactRollback;
+
+ pgStatBackendXactCommit = pgStatBackendXactRollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +317,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +336,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +441,22 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ pgStatBackendXactCommit++;
+ else
+ pgStatBackendXactRollback++;
+
+ backend_has_xactstats = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..4c9f6b0dcec 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--ZnbSmJtA6EgjuGCe--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v2] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 166 insertions(+), 1 deletion(-)
26.1% doc/src/sgml/
29.1% src/backend/utils/activity/
11.9% src/backend/utils/adt/
9.4% src/include/catalog/
3.9% src/include/utils/
3.3% src/include/
8.9% src/test/regress/expected/
7.1% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 3f4a27a736e..77b41e02b82 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..12ae9a8d321 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,23 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..d3491faaff2 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Xact statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--SywurcztURg2uk+W--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v3 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. A new function is called in AtEOXact_PgStat() to increment those
two new counters.
---
src/backend/utils/activity/pgstat_backend.c | 57 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
4 files changed, 69 insertions(+), 1 deletion(-)
78.9% src/backend/utils/activity/
11.2% src/include/utils/
9.8% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..bf164854c4b 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,20 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /* Count transaction commit or abort */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..9efc0e10ebf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
--
2.34.1
--u6+Li7FU79Mt/lVd
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v3-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v4 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..47ce61e5093 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -36,7 +36,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index b31f20d41bc..400a8c5d734 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -342,6 +342,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..0fdbaf79780 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -801,6 +809,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..b97184c6539 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,7 +616,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--jg9kqX3osLGO+8A7
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v4-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v5 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 7727fed3bda..42bfb9cd38f 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -37,7 +37,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -48,6 +48,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -261,6 +266,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -285,6 +318,10 @@ pgstat_flush_backend(bool nowait, uint32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -300,6 +337,9 @@ pgstat_flush_backend(bool nowait, uint32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index 933dcb5cae5..218ff5e653c 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -352,6 +352,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 8e3549c3752..5d73c4dfbcf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -523,6 +523,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -535,6 +537,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -844,6 +852,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index eed4c6b359c..8077c65e938 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -707,7 +707,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, uint32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--uN5gGi2rtCNSVbpC
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v5-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v5 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 7727fed3bda..42bfb9cd38f 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -37,7 +37,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -48,6 +48,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -261,6 +266,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -285,6 +318,10 @@ pgstat_flush_backend(bool nowait, uint32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -300,6 +337,9 @@ pgstat_flush_backend(bool nowait, uint32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index 933dcb5cae5..218ff5e653c 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -352,6 +352,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 8e3549c3752..5d73c4dfbcf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -523,6 +523,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -535,6 +537,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -844,6 +852,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index eed4c6b359c..8077c65e938 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -707,7 +707,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, uint32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--uN5gGi2rtCNSVbpC
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v5-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v1] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 2 +
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 160 insertions(+), 1 deletion(-)
26.9% doc/src/sgml/
29.1% src/backend/utils/activity/
12.3% src/backend/utils/adt/
9.7% src/include/catalog/
4.0% src/include/utils/
9.2% src/test/regress/expected/
7.3% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index fa78031ccbb..ef89683164f 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..7cec0c83071 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,13 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static int pgStatBackendXactCommit = 0;
+static int pgStatBackendXactRollback = 0;
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +266,33 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += pgStatBackendXactCommit;
+ shbackendent->stats.xact_rollback += pgStatBackendXactRollback;
+
+ pgStatBackendXactCommit = pgStatBackendXactRollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +317,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +336,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +441,22 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ pgStatBackendXactCommit++;
+ else
+ pgStatBackendXactRollback++;
+
+ backend_has_xactstats = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..4c9f6b0dcec 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--ZnbSmJtA6EgjuGCe--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v2] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 166 insertions(+), 1 deletion(-)
26.1% doc/src/sgml/
29.1% src/backend/utils/activity/
11.9% src/backend/utils/adt/
9.4% src/include/catalog/
3.9% src/include/utils/
3.3% src/include/
8.9% src/test/regress/expected/
7.1% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 3f4a27a736e..77b41e02b82 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..12ae9a8d321 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,23 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..d3491faaff2 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Xact statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--SywurcztURg2uk+W--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v3 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. A new function is called in AtEOXact_PgStat() to increment those
two new counters.
---
src/backend/utils/activity/pgstat_backend.c | 57 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
4 files changed, 69 insertions(+), 1 deletion(-)
78.9% src/backend/utils/activity/
11.2% src/include/utils/
9.8% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..bf164854c4b 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,20 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /* Count transaction commit or abort */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..9efc0e10ebf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
--
2.34.1
--u6+Li7FU79Mt/lVd
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v3-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v4 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..47ce61e5093 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -36,7 +36,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index b31f20d41bc..400a8c5d734 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -342,6 +342,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..0fdbaf79780 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -801,6 +809,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..b97184c6539 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,7 +616,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--jg9kqX3osLGO+8A7
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v4-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v5 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 7727fed3bda..42bfb9cd38f 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -37,7 +37,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -48,6 +48,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -261,6 +266,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -285,6 +318,10 @@ pgstat_flush_backend(bool nowait, uint32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -300,6 +337,9 @@ pgstat_flush_backend(bool nowait, uint32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index 933dcb5cae5..218ff5e653c 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -352,6 +352,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 8e3549c3752..5d73c4dfbcf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -523,6 +523,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -535,6 +537,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -844,6 +852,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index eed4c6b359c..8077c65e938 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -707,7 +707,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, uint32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--uN5gGi2rtCNSVbpC
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v5-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v1] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 2 +
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 160 insertions(+), 1 deletion(-)
26.9% doc/src/sgml/
29.1% src/backend/utils/activity/
12.3% src/backend/utils/adt/
9.7% src/include/catalog/
4.0% src/include/utils/
9.2% src/test/regress/expected/
7.3% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index fa78031ccbb..ef89683164f 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..7cec0c83071 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,13 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static int pgStatBackendXactCommit = 0;
+static int pgStatBackendXactRollback = 0;
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +266,33 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += pgStatBackendXactCommit;
+ shbackendent->stats.xact_rollback += pgStatBackendXactRollback;
+
+ pgStatBackendXactCommit = pgStatBackendXactRollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +317,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +336,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +441,22 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ pgStatBackendXactCommit++;
+ else
+ pgStatBackendXactRollback++;
+
+ backend_has_xactstats = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..4c9f6b0dcec 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--ZnbSmJtA6EgjuGCe--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v2] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 166 insertions(+), 1 deletion(-)
26.1% doc/src/sgml/
29.1% src/backend/utils/activity/
11.9% src/backend/utils/adt/
9.4% src/include/catalog/
3.9% src/include/utils/
3.3% src/include/
8.9% src/test/regress/expected/
7.1% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 3f4a27a736e..77b41e02b82 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..12ae9a8d321 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,23 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..d3491faaff2 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Xact statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--SywurcztURg2uk+W--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v3 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. A new function is called in AtEOXact_PgStat() to increment those
two new counters.
---
src/backend/utils/activity/pgstat_backend.c | 57 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
4 files changed, 69 insertions(+), 1 deletion(-)
78.9% src/backend/utils/activity/
11.2% src/include/utils/
9.8% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..bf164854c4b 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,20 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /* Count transaction commit or abort */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..9efc0e10ebf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
--
2.34.1
--u6+Li7FU79Mt/lVd
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v3-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v4 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..47ce61e5093 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -36,7 +36,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index b31f20d41bc..400a8c5d734 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -342,6 +342,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..0fdbaf79780 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -801,6 +809,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..b97184c6539 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,7 +616,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--jg9kqX3osLGO+8A7
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v4-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v5 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 7727fed3bda..42bfb9cd38f 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -37,7 +37,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -48,6 +48,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -261,6 +266,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -285,6 +318,10 @@ pgstat_flush_backend(bool nowait, uint32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -300,6 +337,9 @@ pgstat_flush_backend(bool nowait, uint32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index 933dcb5cae5..218ff5e653c 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -352,6 +352,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 8e3549c3752..5d73c4dfbcf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -523,6 +523,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -535,6 +537,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -844,6 +852,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index eed4c6b359c..8077c65e938 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -707,7 +707,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, uint32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--uN5gGi2rtCNSVbpC
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v5-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v1] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 2 +
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 160 insertions(+), 1 deletion(-)
26.9% doc/src/sgml/
29.1% src/backend/utils/activity/
12.3% src/backend/utils/adt/
9.7% src/include/catalog/
4.0% src/include/utils/
9.2% src/test/regress/expected/
7.3% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index fa78031ccbb..ef89683164f 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..7cec0c83071 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,13 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static int pgStatBackendXactCommit = 0;
+static int pgStatBackendXactRollback = 0;
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +266,33 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += pgStatBackendXactCommit;
+ shbackendent->stats.xact_rollback += pgStatBackendXactRollback;
+
+ pgStatBackendXactCommit = pgStatBackendXactRollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +317,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +336,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +441,22 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ pgStatBackendXactCommit++;
+ else
+ pgStatBackendXactRollback++;
+
+ backend_has_xactstats = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..4c9f6b0dcec 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--ZnbSmJtA6EgjuGCe--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v2] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 166 insertions(+), 1 deletion(-)
26.1% doc/src/sgml/
29.1% src/backend/utils/activity/
11.9% src/backend/utils/adt/
9.4% src/include/catalog/
3.9% src/include/utils/
3.3% src/include/
8.9% src/test/regress/expected/
7.1% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 3f4a27a736e..77b41e02b82 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..12ae9a8d321 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,23 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..d3491faaff2 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Xact statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--SywurcztURg2uk+W--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v3 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. A new function is called in AtEOXact_PgStat() to increment those
two new counters.
---
src/backend/utils/activity/pgstat_backend.c | 57 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
4 files changed, 69 insertions(+), 1 deletion(-)
78.9% src/backend/utils/activity/
11.2% src/include/utils/
9.8% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..bf164854c4b 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,20 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /* Count transaction commit or abort */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..9efc0e10ebf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
--
2.34.1
--u6+Li7FU79Mt/lVd
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v3-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v4 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..47ce61e5093 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -36,7 +36,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index b31f20d41bc..400a8c5d734 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -342,6 +342,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..0fdbaf79780 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -801,6 +809,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..b97184c6539 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,7 +616,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--jg9kqX3osLGO+8A7
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v4-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v5 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 7727fed3bda..42bfb9cd38f 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -37,7 +37,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -48,6 +48,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -261,6 +266,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -285,6 +318,10 @@ pgstat_flush_backend(bool nowait, uint32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -300,6 +337,9 @@ pgstat_flush_backend(bool nowait, uint32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index 933dcb5cae5..218ff5e653c 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -352,6 +352,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 8e3549c3752..5d73c4dfbcf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -523,6 +523,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -535,6 +537,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -844,6 +852,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index eed4c6b359c..8077c65e938 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -707,7 +707,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, uint32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--uN5gGi2rtCNSVbpC
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v5-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v1] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 2 +
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 160 insertions(+), 1 deletion(-)
26.9% doc/src/sgml/
29.1% src/backend/utils/activity/
12.3% src/backend/utils/adt/
9.7% src/include/catalog/
4.0% src/include/utils/
9.2% src/test/regress/expected/
7.3% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index fa78031ccbb..ef89683164f 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..7cec0c83071 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,13 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static int pgStatBackendXactCommit = 0;
+static int pgStatBackendXactRollback = 0;
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +266,33 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += pgStatBackendXactCommit;
+ shbackendent->stats.xact_rollback += pgStatBackendXactRollback;
+
+ pgStatBackendXactCommit = pgStatBackendXactRollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +317,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +336,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +441,22 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ pgStatBackendXactCommit++;
+ else
+ pgStatBackendXactRollback++;
+
+ backend_has_xactstats = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..4c9f6b0dcec 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--ZnbSmJtA6EgjuGCe--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v2] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 166 insertions(+), 1 deletion(-)
26.1% doc/src/sgml/
29.1% src/backend/utils/activity/
11.9% src/backend/utils/adt/
9.4% src/include/catalog/
3.9% src/include/utils/
3.3% src/include/
8.9% src/test/regress/expected/
7.1% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 3f4a27a736e..77b41e02b82 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..12ae9a8d321 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,23 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..d3491faaff2 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Xact statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--SywurcztURg2uk+W--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v3 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. A new function is called in AtEOXact_PgStat() to increment those
two new counters.
---
src/backend/utils/activity/pgstat_backend.c | 57 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
4 files changed, 69 insertions(+), 1 deletion(-)
78.9% src/backend/utils/activity/
11.2% src/include/utils/
9.8% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..bf164854c4b 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,20 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /* Count transaction commit or abort */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..9efc0e10ebf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
--
2.34.1
--u6+Li7FU79Mt/lVd
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v3-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v4 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..47ce61e5093 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -36,7 +36,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index b31f20d41bc..400a8c5d734 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -342,6 +342,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..0fdbaf79780 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -801,6 +809,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..b97184c6539 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,7 +616,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--jg9kqX3osLGO+8A7
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v4-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v5 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 7727fed3bda..42bfb9cd38f 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -37,7 +37,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -48,6 +48,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -261,6 +266,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -285,6 +318,10 @@ pgstat_flush_backend(bool nowait, uint32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -300,6 +337,9 @@ pgstat_flush_backend(bool nowait, uint32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index 933dcb5cae5..218ff5e653c 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -352,6 +352,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 8e3549c3752..5d73c4dfbcf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -523,6 +523,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -535,6 +537,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -844,6 +852,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index eed4c6b359c..8077c65e938 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -707,7 +707,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, uint32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--uN5gGi2rtCNSVbpC
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v5-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v1] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 2 +
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 160 insertions(+), 1 deletion(-)
26.9% doc/src/sgml/
29.1% src/backend/utils/activity/
12.3% src/backend/utils/adt/
9.7% src/include/catalog/
4.0% src/include/utils/
9.2% src/test/regress/expected/
7.3% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index fa78031ccbb..ef89683164f 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..7cec0c83071 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,13 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static int pgStatBackendXactCommit = 0;
+static int pgStatBackendXactRollback = 0;
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +266,33 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += pgStatBackendXactCommit;
+ shbackendent->stats.xact_rollback += pgStatBackendXactRollback;
+
+ pgStatBackendXactCommit = pgStatBackendXactRollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +317,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +336,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +441,22 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ pgStatBackendXactCommit++;
+ else
+ pgStatBackendXactRollback++;
+
+ backend_has_xactstats = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..4c9f6b0dcec 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--ZnbSmJtA6EgjuGCe--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v2] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 166 insertions(+), 1 deletion(-)
26.1% doc/src/sgml/
29.1% src/backend/utils/activity/
11.9% src/backend/utils/adt/
9.4% src/include/catalog/
3.9% src/include/utils/
3.3% src/include/
8.9% src/test/regress/expected/
7.1% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 3f4a27a736e..77b41e02b82 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..12ae9a8d321 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,23 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..d3491faaff2 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Xact statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--SywurcztURg2uk+W--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v3 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. A new function is called in AtEOXact_PgStat() to increment those
two new counters.
---
src/backend/utils/activity/pgstat_backend.c | 57 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
4 files changed, 69 insertions(+), 1 deletion(-)
78.9% src/backend/utils/activity/
11.2% src/include/utils/
9.8% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..bf164854c4b 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,20 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /* Count transaction commit or abort */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..9efc0e10ebf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
--
2.34.1
--u6+Li7FU79Mt/lVd
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v3-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v4 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..47ce61e5093 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -36,7 +36,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index b31f20d41bc..400a8c5d734 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -342,6 +342,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..0fdbaf79780 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -801,6 +809,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..b97184c6539 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,7 +616,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--jg9kqX3osLGO+8A7
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v4-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v5 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 7727fed3bda..42bfb9cd38f 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -37,7 +37,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -48,6 +48,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -261,6 +266,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -285,6 +318,10 @@ pgstat_flush_backend(bool nowait, uint32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -300,6 +337,9 @@ pgstat_flush_backend(bool nowait, uint32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index 933dcb5cae5..218ff5e653c 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -352,6 +352,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 8e3549c3752..5d73c4dfbcf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -523,6 +523,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -535,6 +537,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -844,6 +852,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index eed4c6b359c..8077c65e938 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -707,7 +707,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, uint32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--uN5gGi2rtCNSVbpC
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v5-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v1] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 2 +
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 160 insertions(+), 1 deletion(-)
26.9% doc/src/sgml/
29.1% src/backend/utils/activity/
12.3% src/backend/utils/adt/
9.7% src/include/catalog/
4.0% src/include/utils/
9.2% src/test/regress/expected/
7.3% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index fa78031ccbb..ef89683164f 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..7cec0c83071 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,13 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static int pgStatBackendXactCommit = 0;
+static int pgStatBackendXactRollback = 0;
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +266,33 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += pgStatBackendXactCommit;
+ shbackendent->stats.xact_rollback += pgStatBackendXactRollback;
+
+ pgStatBackendXactCommit = pgStatBackendXactRollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +317,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +336,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +441,22 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ pgStatBackendXactCommit++;
+ else
+ pgStatBackendXactRollback++;
+
+ backend_has_xactstats = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..4c9f6b0dcec 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--ZnbSmJtA6EgjuGCe--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v2] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 166 insertions(+), 1 deletion(-)
26.1% doc/src/sgml/
29.1% src/backend/utils/activity/
11.9% src/backend/utils/adt/
9.4% src/include/catalog/
3.9% src/include/utils/
3.3% src/include/
8.9% src/test/regress/expected/
7.1% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 3f4a27a736e..77b41e02b82 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..12ae9a8d321 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,23 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..d3491faaff2 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Xact statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--SywurcztURg2uk+W--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v3 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. A new function is called in AtEOXact_PgStat() to increment those
two new counters.
---
src/backend/utils/activity/pgstat_backend.c | 57 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
4 files changed, 69 insertions(+), 1 deletion(-)
78.9% src/backend/utils/activity/
11.2% src/include/utils/
9.8% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..bf164854c4b 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,20 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /* Count transaction commit or abort */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..9efc0e10ebf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
--
2.34.1
--u6+Li7FU79Mt/lVd
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v3-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v4 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..47ce61e5093 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -36,7 +36,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index b31f20d41bc..400a8c5d734 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -342,6 +342,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..0fdbaf79780 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -801,6 +809,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..b97184c6539 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,7 +616,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--jg9kqX3osLGO+8A7
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v4-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v5 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 7727fed3bda..42bfb9cd38f 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -37,7 +37,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -48,6 +48,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -261,6 +266,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -285,6 +318,10 @@ pgstat_flush_backend(bool nowait, uint32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -300,6 +337,9 @@ pgstat_flush_backend(bool nowait, uint32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index 933dcb5cae5..218ff5e653c 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -352,6 +352,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 8e3549c3752..5d73c4dfbcf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -523,6 +523,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -535,6 +537,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -844,6 +852,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index eed4c6b359c..8077c65e938 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -707,7 +707,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, uint32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--uN5gGi2rtCNSVbpC
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v5-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v1] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 2 +
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 160 insertions(+), 1 deletion(-)
26.9% doc/src/sgml/
29.1% src/backend/utils/activity/
12.3% src/backend/utils/adt/
9.7% src/include/catalog/
4.0% src/include/utils/
9.2% src/test/regress/expected/
7.3% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index fa78031ccbb..ef89683164f 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..7cec0c83071 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,13 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static int pgStatBackendXactCommit = 0;
+static int pgStatBackendXactRollback = 0;
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +266,33 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += pgStatBackendXactCommit;
+ shbackendent->stats.xact_rollback += pgStatBackendXactRollback;
+
+ pgStatBackendXactCommit = pgStatBackendXactRollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +317,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +336,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +441,22 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ pgStatBackendXactCommit++;
+ else
+ pgStatBackendXactRollback++;
+
+ backend_has_xactstats = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..4c9f6b0dcec 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--ZnbSmJtA6EgjuGCe--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v2] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 166 insertions(+), 1 deletion(-)
26.1% doc/src/sgml/
29.1% src/backend/utils/activity/
11.9% src/backend/utils/adt/
9.4% src/include/catalog/
3.9% src/include/utils/
3.3% src/include/
8.9% src/test/regress/expected/
7.1% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 3f4a27a736e..77b41e02b82 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..12ae9a8d321 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,23 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..d3491faaff2 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Xact statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--SywurcztURg2uk+W--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v3 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. A new function is called in AtEOXact_PgStat() to increment those
two new counters.
---
src/backend/utils/activity/pgstat_backend.c | 57 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
4 files changed, 69 insertions(+), 1 deletion(-)
78.9% src/backend/utils/activity/
11.2% src/include/utils/
9.8% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..bf164854c4b 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,20 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /* Count transaction commit or abort */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..9efc0e10ebf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
--
2.34.1
--u6+Li7FU79Mt/lVd
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v3-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v4 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..47ce61e5093 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -36,7 +36,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index b31f20d41bc..400a8c5d734 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -342,6 +342,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..0fdbaf79780 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -801,6 +809,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..b97184c6539 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,7 +616,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--jg9kqX3osLGO+8A7
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v4-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v5 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 7727fed3bda..42bfb9cd38f 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -37,7 +37,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -48,6 +48,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -261,6 +266,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -285,6 +318,10 @@ pgstat_flush_backend(bool nowait, uint32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -300,6 +337,9 @@ pgstat_flush_backend(bool nowait, uint32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index 933dcb5cae5..218ff5e653c 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -352,6 +352,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 8e3549c3752..5d73c4dfbcf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -523,6 +523,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -535,6 +537,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -844,6 +852,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index eed4c6b359c..8077c65e938 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -707,7 +707,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, uint32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--uN5gGi2rtCNSVbpC
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v5-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v1] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 2 +
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 160 insertions(+), 1 deletion(-)
26.9% doc/src/sgml/
29.1% src/backend/utils/activity/
12.3% src/backend/utils/adt/
9.7% src/include/catalog/
4.0% src/include/utils/
9.2% src/test/regress/expected/
7.3% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index fa78031ccbb..ef89683164f 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..7cec0c83071 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,13 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static int pgStatBackendXactCommit = 0;
+static int pgStatBackendXactRollback = 0;
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +266,33 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += pgStatBackendXactCommit;
+ shbackendent->stats.xact_rollback += pgStatBackendXactRollback;
+
+ pgStatBackendXactCommit = pgStatBackendXactRollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +317,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +336,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +441,22 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ pgStatBackendXactCommit++;
+ else
+ pgStatBackendXactRollback++;
+
+ backend_has_xactstats = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..4c9f6b0dcec 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--ZnbSmJtA6EgjuGCe--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v2] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 166 insertions(+), 1 deletion(-)
26.1% doc/src/sgml/
29.1% src/backend/utils/activity/
11.9% src/backend/utils/adt/
9.4% src/include/catalog/
3.9% src/include/utils/
3.3% src/include/
8.9% src/test/regress/expected/
7.1% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 3f4a27a736e..77b41e02b82 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..12ae9a8d321 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,23 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..d3491faaff2 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Xact statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--SywurcztURg2uk+W--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v3 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. A new function is called in AtEOXact_PgStat() to increment those
two new counters.
---
src/backend/utils/activity/pgstat_backend.c | 57 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
4 files changed, 69 insertions(+), 1 deletion(-)
78.9% src/backend/utils/activity/
11.2% src/include/utils/
9.8% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..bf164854c4b 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,20 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /* Count transaction commit or abort */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..9efc0e10ebf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
--
2.34.1
--u6+Li7FU79Mt/lVd
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v3-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v4 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..47ce61e5093 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -36,7 +36,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index b31f20d41bc..400a8c5d734 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -342,6 +342,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..0fdbaf79780 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -801,6 +809,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..b97184c6539 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,7 +616,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--jg9kqX3osLGO+8A7
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v4-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v5 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 7727fed3bda..42bfb9cd38f 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -37,7 +37,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -48,6 +48,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -261,6 +266,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -285,6 +318,10 @@ pgstat_flush_backend(bool nowait, uint32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -300,6 +337,9 @@ pgstat_flush_backend(bool nowait, uint32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index 933dcb5cae5..218ff5e653c 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -352,6 +352,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 8e3549c3752..5d73c4dfbcf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -523,6 +523,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -535,6 +537,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -844,6 +852,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index eed4c6b359c..8077c65e938 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -707,7 +707,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, uint32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--uN5gGi2rtCNSVbpC
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v5-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v1] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 2 +
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 160 insertions(+), 1 deletion(-)
26.9% doc/src/sgml/
29.1% src/backend/utils/activity/
12.3% src/backend/utils/adt/
9.7% src/include/catalog/
4.0% src/include/utils/
9.2% src/test/regress/expected/
7.3% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index fa78031ccbb..ef89683164f 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..7cec0c83071 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,13 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static int pgStatBackendXactCommit = 0;
+static int pgStatBackendXactRollback = 0;
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +266,33 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += pgStatBackendXactCommit;
+ shbackendent->stats.xact_rollback += pgStatBackendXactRollback;
+
+ pgStatBackendXactCommit = pgStatBackendXactRollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +317,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +336,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +441,22 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ pgStatBackendXactCommit++;
+ else
+ pgStatBackendXactRollback++;
+
+ backend_has_xactstats = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..4c9f6b0dcec 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--ZnbSmJtA6EgjuGCe--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v2] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 166 insertions(+), 1 deletion(-)
26.1% doc/src/sgml/
29.1% src/backend/utils/activity/
11.9% src/backend/utils/adt/
9.4% src/include/catalog/
3.9% src/include/utils/
3.3% src/include/
8.9% src/test/regress/expected/
7.1% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 3f4a27a736e..77b41e02b82 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..12ae9a8d321 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,23 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..d3491faaff2 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Xact statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--SywurcztURg2uk+W--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v3 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. A new function is called in AtEOXact_PgStat() to increment those
two new counters.
---
src/backend/utils/activity/pgstat_backend.c | 57 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
4 files changed, 69 insertions(+), 1 deletion(-)
78.9% src/backend/utils/activity/
11.2% src/include/utils/
9.8% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..bf164854c4b 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,20 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /* Count transaction commit or abort */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..9efc0e10ebf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
--
2.34.1
--u6+Li7FU79Mt/lVd
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v3-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v4 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..47ce61e5093 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -36,7 +36,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index b31f20d41bc..400a8c5d734 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -342,6 +342,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..0fdbaf79780 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -801,6 +809,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..b97184c6539 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,7 +616,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--jg9kqX3osLGO+8A7
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v4-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v5 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 7727fed3bda..42bfb9cd38f 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -37,7 +37,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -48,6 +48,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -261,6 +266,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -285,6 +318,10 @@ pgstat_flush_backend(bool nowait, uint32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -300,6 +337,9 @@ pgstat_flush_backend(bool nowait, uint32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index 933dcb5cae5..218ff5e653c 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -352,6 +352,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 8e3549c3752..5d73c4dfbcf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -523,6 +523,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -535,6 +537,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -844,6 +852,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index eed4c6b359c..8077c65e938 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -707,7 +707,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, uint32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--uN5gGi2rtCNSVbpC
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v5-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v1] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 2 +
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 160 insertions(+), 1 deletion(-)
26.9% doc/src/sgml/
29.1% src/backend/utils/activity/
12.3% src/backend/utils/adt/
9.7% src/include/catalog/
4.0% src/include/utils/
9.2% src/test/regress/expected/
7.3% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index fa78031ccbb..ef89683164f 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..7cec0c83071 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,13 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static int pgStatBackendXactCommit = 0;
+static int pgStatBackendXactRollback = 0;
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +266,33 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += pgStatBackendXactCommit;
+ shbackendent->stats.xact_rollback += pgStatBackendXactRollback;
+
+ pgStatBackendXactCommit = pgStatBackendXactRollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +317,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +336,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +441,22 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ pgStatBackendXactCommit++;
+ else
+ pgStatBackendXactRollback++;
+
+ backend_has_xactstats = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..4c9f6b0dcec 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--ZnbSmJtA6EgjuGCe--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v2] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 166 insertions(+), 1 deletion(-)
26.1% doc/src/sgml/
29.1% src/backend/utils/activity/
11.9% src/backend/utils/adt/
9.4% src/include/catalog/
3.9% src/include/utils/
3.3% src/include/
8.9% src/test/regress/expected/
7.1% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 3f4a27a736e..77b41e02b82 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..12ae9a8d321 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,23 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..d3491faaff2 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Xact statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--SywurcztURg2uk+W--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v3 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. A new function is called in AtEOXact_PgStat() to increment those
two new counters.
---
src/backend/utils/activity/pgstat_backend.c | 57 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
4 files changed, 69 insertions(+), 1 deletion(-)
78.9% src/backend/utils/activity/
11.2% src/include/utils/
9.8% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..bf164854c4b 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,20 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /* Count transaction commit or abort */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..9efc0e10ebf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
--
2.34.1
--u6+Li7FU79Mt/lVd
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v3-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v4 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..47ce61e5093 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -36,7 +36,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index b31f20d41bc..400a8c5d734 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -342,6 +342,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..0fdbaf79780 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -801,6 +809,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..b97184c6539 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,7 +616,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--jg9kqX3osLGO+8A7
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v4-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v5 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 7727fed3bda..42bfb9cd38f 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -37,7 +37,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -48,6 +48,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -261,6 +266,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -285,6 +318,10 @@ pgstat_flush_backend(bool nowait, uint32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -300,6 +337,9 @@ pgstat_flush_backend(bool nowait, uint32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index 933dcb5cae5..218ff5e653c 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -352,6 +352,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 8e3549c3752..5d73c4dfbcf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -523,6 +523,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -535,6 +537,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -844,6 +852,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index eed4c6b359c..8077c65e938 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -707,7 +707,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, uint32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--uN5gGi2rtCNSVbpC
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v5-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v2] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 166 insertions(+), 1 deletion(-)
26.1% doc/src/sgml/
29.1% src/backend/utils/activity/
11.9% src/backend/utils/adt/
9.4% src/include/catalog/
3.9% src/include/utils/
3.3% src/include/
8.9% src/test/regress/expected/
7.1% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 3f4a27a736e..77b41e02b82 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..12ae9a8d321 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,23 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..d3491faaff2 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Xact statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--SywurcztURg2uk+W--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v3 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. A new function is called in AtEOXact_PgStat() to increment those
two new counters.
---
src/backend/utils/activity/pgstat_backend.c | 57 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
4 files changed, 69 insertions(+), 1 deletion(-)
78.9% src/backend/utils/activity/
11.2% src/include/utils/
9.8% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..bf164854c4b 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,20 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /* Count transaction commit or abort */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..9efc0e10ebf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
--
2.34.1
--u6+Li7FU79Mt/lVd
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v3-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v4 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..47ce61e5093 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -36,7 +36,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index b31f20d41bc..400a8c5d734 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -342,6 +342,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..0fdbaf79780 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -801,6 +809,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..b97184c6539 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,7 +616,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--jg9kqX3osLGO+8A7
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v4-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v5 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 7727fed3bda..42bfb9cd38f 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -37,7 +37,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -48,6 +48,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -261,6 +266,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -285,6 +318,10 @@ pgstat_flush_backend(bool nowait, uint32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -300,6 +337,9 @@ pgstat_flush_backend(bool nowait, uint32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index 933dcb5cae5..218ff5e653c 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -352,6 +352,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 8e3549c3752..5d73c4dfbcf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -523,6 +523,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -535,6 +537,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -844,6 +852,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index eed4c6b359c..8077c65e938 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -707,7 +707,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, uint32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--uN5gGi2rtCNSVbpC
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v5-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v1] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 2 +
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 160 insertions(+), 1 deletion(-)
26.9% doc/src/sgml/
29.1% src/backend/utils/activity/
12.3% src/backend/utils/adt/
9.7% src/include/catalog/
4.0% src/include/utils/
9.2% src/test/regress/expected/
7.3% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index fa78031ccbb..ef89683164f 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..7cec0c83071 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,13 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static int pgStatBackendXactCommit = 0;
+static int pgStatBackendXactRollback = 0;
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +266,33 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += pgStatBackendXactCommit;
+ shbackendent->stats.xact_rollback += pgStatBackendXactRollback;
+
+ pgStatBackendXactCommit = pgStatBackendXactRollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +317,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +336,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +441,22 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ pgStatBackendXactCommit++;
+ else
+ pgStatBackendXactRollback++;
+
+ backend_has_xactstats = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..4c9f6b0dcec 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--ZnbSmJtA6EgjuGCe--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v2] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 166 insertions(+), 1 deletion(-)
26.1% doc/src/sgml/
29.1% src/backend/utils/activity/
11.9% src/backend/utils/adt/
9.4% src/include/catalog/
3.9% src/include/utils/
3.3% src/include/
8.9% src/test/regress/expected/
7.1% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 3f4a27a736e..77b41e02b82 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..12ae9a8d321 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,23 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..d3491faaff2 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Xact statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--SywurcztURg2uk+W--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v3 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. A new function is called in AtEOXact_PgStat() to increment those
two new counters.
---
src/backend/utils/activity/pgstat_backend.c | 57 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
4 files changed, 69 insertions(+), 1 deletion(-)
78.9% src/backend/utils/activity/
11.2% src/include/utils/
9.8% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..bf164854c4b 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,20 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /* Count transaction commit or abort */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..9efc0e10ebf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
--
2.34.1
--u6+Li7FU79Mt/lVd
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v3-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v4 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..47ce61e5093 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -36,7 +36,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index b31f20d41bc..400a8c5d734 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -342,6 +342,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..0fdbaf79780 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -801,6 +809,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..b97184c6539 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,7 +616,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--jg9kqX3osLGO+8A7
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v4-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v5 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 7727fed3bda..42bfb9cd38f 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -37,7 +37,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -48,6 +48,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -261,6 +266,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -285,6 +318,10 @@ pgstat_flush_backend(bool nowait, uint32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -300,6 +337,9 @@ pgstat_flush_backend(bool nowait, uint32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index 933dcb5cae5..218ff5e653c 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -352,6 +352,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 8e3549c3752..5d73c4dfbcf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -523,6 +523,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -535,6 +537,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -844,6 +852,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index eed4c6b359c..8077c65e938 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -707,7 +707,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, uint32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--uN5gGi2rtCNSVbpC
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v5-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v1] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 2 +
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 160 insertions(+), 1 deletion(-)
26.9% doc/src/sgml/
29.1% src/backend/utils/activity/
12.3% src/backend/utils/adt/
9.7% src/include/catalog/
4.0% src/include/utils/
9.2% src/test/regress/expected/
7.3% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index fa78031ccbb..ef89683164f 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..7cec0c83071 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,13 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static int pgStatBackendXactCommit = 0;
+static int pgStatBackendXactRollback = 0;
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +266,33 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += pgStatBackendXactCommit;
+ shbackendent->stats.xact_rollback += pgStatBackendXactRollback;
+
+ pgStatBackendXactCommit = pgStatBackendXactRollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +317,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +336,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +441,22 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ pgStatBackendXactCommit++;
+ else
+ pgStatBackendXactRollback++;
+
+ backend_has_xactstats = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..4c9f6b0dcec 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--ZnbSmJtA6EgjuGCe--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v2] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 166 insertions(+), 1 deletion(-)
26.1% doc/src/sgml/
29.1% src/backend/utils/activity/
11.9% src/backend/utils/adt/
9.4% src/include/catalog/
3.9% src/include/utils/
3.3% src/include/
8.9% src/test/regress/expected/
7.1% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 3f4a27a736e..77b41e02b82 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..12ae9a8d321 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,23 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..d3491faaff2 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Xact statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--SywurcztURg2uk+W--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v3 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. A new function is called in AtEOXact_PgStat() to increment those
two new counters.
---
src/backend/utils/activity/pgstat_backend.c | 57 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
4 files changed, 69 insertions(+), 1 deletion(-)
78.9% src/backend/utils/activity/
11.2% src/include/utils/
9.8% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..bf164854c4b 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,20 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /* Count transaction commit or abort */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..9efc0e10ebf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
--
2.34.1
--u6+Li7FU79Mt/lVd
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v3-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v4 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..47ce61e5093 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -36,7 +36,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index b31f20d41bc..400a8c5d734 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -342,6 +342,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..0fdbaf79780 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -801,6 +809,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..b97184c6539 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,7 +616,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--jg9kqX3osLGO+8A7
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v4-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v5 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 7727fed3bda..42bfb9cd38f 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -37,7 +37,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -48,6 +48,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -261,6 +266,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -285,6 +318,10 @@ pgstat_flush_backend(bool nowait, uint32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -300,6 +337,9 @@ pgstat_flush_backend(bool nowait, uint32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index 933dcb5cae5..218ff5e653c 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -352,6 +352,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 8e3549c3752..5d73c4dfbcf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -523,6 +523,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -535,6 +537,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -844,6 +852,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index eed4c6b359c..8077c65e938 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -707,7 +707,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, uint32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--uN5gGi2rtCNSVbpC
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v5-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v1] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 2 +
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 160 insertions(+), 1 deletion(-)
26.9% doc/src/sgml/
29.1% src/backend/utils/activity/
12.3% src/backend/utils/adt/
9.7% src/include/catalog/
4.0% src/include/utils/
9.2% src/test/regress/expected/
7.3% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index fa78031ccbb..ef89683164f 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..7cec0c83071 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,13 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static int pgStatBackendXactCommit = 0;
+static int pgStatBackendXactRollback = 0;
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +266,33 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += pgStatBackendXactCommit;
+ shbackendent->stats.xact_rollback += pgStatBackendXactRollback;
+
+ pgStatBackendXactCommit = pgStatBackendXactRollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +317,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +336,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +441,22 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ pgStatBackendXactCommit++;
+ else
+ pgStatBackendXactRollback++;
+
+ backend_has_xactstats = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..4c9f6b0dcec 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--ZnbSmJtA6EgjuGCe--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v4 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..47ce61e5093 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -36,7 +36,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index b31f20d41bc..400a8c5d734 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -342,6 +342,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..0fdbaf79780 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -801,6 +809,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..b97184c6539 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,7 +616,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--jg9kqX3osLGO+8A7
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v4-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v5 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 7727fed3bda..42bfb9cd38f 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -37,7 +37,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -48,6 +48,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -261,6 +266,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -285,6 +318,10 @@ pgstat_flush_backend(bool nowait, uint32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -300,6 +337,9 @@ pgstat_flush_backend(bool nowait, uint32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index 933dcb5cae5..218ff5e653c 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -352,6 +352,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 8e3549c3752..5d73c4dfbcf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -523,6 +523,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -535,6 +537,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -844,6 +852,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index eed4c6b359c..8077c65e938 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -707,7 +707,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, uint32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--uN5gGi2rtCNSVbpC
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v5-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v1] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 2 +
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 160 insertions(+), 1 deletion(-)
26.9% doc/src/sgml/
29.1% src/backend/utils/activity/
12.3% src/backend/utils/adt/
9.7% src/include/catalog/
4.0% src/include/utils/
9.2% src/test/regress/expected/
7.3% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index fa78031ccbb..ef89683164f 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..7cec0c83071 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,13 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static int pgStatBackendXactCommit = 0;
+static int pgStatBackendXactRollback = 0;
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +266,33 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += pgStatBackendXactCommit;
+ shbackendent->stats.xact_rollback += pgStatBackendXactRollback;
+
+ pgStatBackendXactCommit = pgStatBackendXactRollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +317,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +336,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +441,22 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ pgStatBackendXactCommit++;
+ else
+ pgStatBackendXactRollback++;
+
+ backend_has_xactstats = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..4c9f6b0dcec 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--ZnbSmJtA6EgjuGCe--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v2] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 166 insertions(+), 1 deletion(-)
26.1% doc/src/sgml/
29.1% src/backend/utils/activity/
11.9% src/backend/utils/adt/
9.4% src/include/catalog/
3.9% src/include/utils/
3.3% src/include/
8.9% src/test/regress/expected/
7.1% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 3f4a27a736e..77b41e02b82 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..12ae9a8d321 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,23 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..d3491faaff2 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Xact statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--SywurcztURg2uk+W--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v3 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. A new function is called in AtEOXact_PgStat() to increment those
two new counters.
---
src/backend/utils/activity/pgstat_backend.c | 57 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
4 files changed, 69 insertions(+), 1 deletion(-)
78.9% src/backend/utils/activity/
11.2% src/include/utils/
9.8% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..bf164854c4b 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,20 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /* Count transaction commit or abort */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..9efc0e10ebf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
--
2.34.1
--u6+Li7FU79Mt/lVd
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v3-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v4 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..47ce61e5093 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -36,7 +36,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index b31f20d41bc..400a8c5d734 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -342,6 +342,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..0fdbaf79780 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -801,6 +809,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..b97184c6539 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,7 +616,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--jg9kqX3osLGO+8A7
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v4-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v1] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 2 +
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 160 insertions(+), 1 deletion(-)
26.9% doc/src/sgml/
29.1% src/backend/utils/activity/
12.3% src/backend/utils/adt/
9.7% src/include/catalog/
4.0% src/include/utils/
9.2% src/test/regress/expected/
7.3% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index fa78031ccbb..ef89683164f 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..7cec0c83071 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,13 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static int pgStatBackendXactCommit = 0;
+static int pgStatBackendXactRollback = 0;
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +266,33 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += pgStatBackendXactCommit;
+ shbackendent->stats.xact_rollback += pgStatBackendXactRollback;
+
+ pgStatBackendXactCommit = pgStatBackendXactRollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +317,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +336,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +441,22 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ pgStatBackendXactCommit++;
+ else
+ pgStatBackendXactRollback++;
+
+ backend_has_xactstats = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..4c9f6b0dcec 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--ZnbSmJtA6EgjuGCe--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v2] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 166 insertions(+), 1 deletion(-)
26.1% doc/src/sgml/
29.1% src/backend/utils/activity/
11.9% src/backend/utils/adt/
9.4% src/include/catalog/
3.9% src/include/utils/
3.3% src/include/
8.9% src/test/regress/expected/
7.1% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 3f4a27a736e..77b41e02b82 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..12ae9a8d321 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,23 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..d3491faaff2 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Xact statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--SywurcztURg2uk+W--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v3 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. A new function is called in AtEOXact_PgStat() to increment those
two new counters.
---
src/backend/utils/activity/pgstat_backend.c | 57 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
4 files changed, 69 insertions(+), 1 deletion(-)
78.9% src/backend/utils/activity/
11.2% src/include/utils/
9.8% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..bf164854c4b 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,20 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /* Count transaction commit or abort */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..9efc0e10ebf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
--
2.34.1
--u6+Li7FU79Mt/lVd
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v3-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v4 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..47ce61e5093 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -36,7 +36,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index b31f20d41bc..400a8c5d734 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -342,6 +342,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..0fdbaf79780 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -801,6 +809,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..b97184c6539 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,7 +616,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--jg9kqX3osLGO+8A7
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v4-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v5 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 7727fed3bda..42bfb9cd38f 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -37,7 +37,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -48,6 +48,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -261,6 +266,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -285,6 +318,10 @@ pgstat_flush_backend(bool nowait, uint32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -300,6 +337,9 @@ pgstat_flush_backend(bool nowait, uint32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index 933dcb5cae5..218ff5e653c 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -352,6 +352,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 8e3549c3752..5d73c4dfbcf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -523,6 +523,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -535,6 +537,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -844,6 +852,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index eed4c6b359c..8077c65e938 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -707,7 +707,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, uint32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--uN5gGi2rtCNSVbpC
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v5-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v1] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 2 +
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 160 insertions(+), 1 deletion(-)
26.9% doc/src/sgml/
29.1% src/backend/utils/activity/
12.3% src/backend/utils/adt/
9.7% src/include/catalog/
4.0% src/include/utils/
9.2% src/test/regress/expected/
7.3% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index fa78031ccbb..ef89683164f 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..7cec0c83071 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,13 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static int pgStatBackendXactCommit = 0;
+static int pgStatBackendXactRollback = 0;
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +266,33 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += pgStatBackendXactCommit;
+ shbackendent->stats.xact_rollback += pgStatBackendXactRollback;
+
+ pgStatBackendXactCommit = pgStatBackendXactRollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +317,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +336,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +441,22 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ pgStatBackendXactCommit++;
+ else
+ pgStatBackendXactRollback++;
+
+ backend_has_xactstats = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..4c9f6b0dcec 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--ZnbSmJtA6EgjuGCe--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v3 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. A new function is called in AtEOXact_PgStat() to increment those
two new counters.
---
src/backend/utils/activity/pgstat_backend.c | 57 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
4 files changed, 69 insertions(+), 1 deletion(-)
78.9% src/backend/utils/activity/
11.2% src/include/utils/
9.8% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..bf164854c4b 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,20 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /* Count transaction commit or abort */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..9efc0e10ebf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
--
2.34.1
--u6+Li7FU79Mt/lVd
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v3-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v4 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..47ce61e5093 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -36,7 +36,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index b31f20d41bc..400a8c5d734 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -342,6 +342,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..0fdbaf79780 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -801,6 +809,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..b97184c6539 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,7 +616,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--jg9kqX3osLGO+8A7
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v4-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v5 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 7727fed3bda..42bfb9cd38f 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -37,7 +37,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -48,6 +48,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -261,6 +266,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -285,6 +318,10 @@ pgstat_flush_backend(bool nowait, uint32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -300,6 +337,9 @@ pgstat_flush_backend(bool nowait, uint32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index 933dcb5cae5..218ff5e653c 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -352,6 +352,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 8e3549c3752..5d73c4dfbcf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -523,6 +523,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -535,6 +537,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -844,6 +852,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index eed4c6b359c..8077c65e938 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -707,7 +707,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, uint32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--uN5gGi2rtCNSVbpC
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v5-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v1] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 2 +
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 160 insertions(+), 1 deletion(-)
26.9% doc/src/sgml/
29.1% src/backend/utils/activity/
12.3% src/backend/utils/adt/
9.7% src/include/catalog/
4.0% src/include/utils/
9.2% src/test/regress/expected/
7.3% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index fa78031ccbb..ef89683164f 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..7cec0c83071 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,13 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static int pgStatBackendXactCommit = 0;
+static int pgStatBackendXactRollback = 0;
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +266,33 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += pgStatBackendXactCommit;
+ shbackendent->stats.xact_rollback += pgStatBackendXactRollback;
+
+ pgStatBackendXactCommit = pgStatBackendXactRollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +317,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +336,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +441,22 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ pgStatBackendXactCommit++;
+ else
+ pgStatBackendXactRollback++;
+
+ backend_has_xactstats = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..4c9f6b0dcec 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--ZnbSmJtA6EgjuGCe--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v2] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 166 insertions(+), 1 deletion(-)
26.1% doc/src/sgml/
29.1% src/backend/utils/activity/
11.9% src/backend/utils/adt/
9.4% src/include/catalog/
3.9% src/include/utils/
3.3% src/include/
8.9% src/test/regress/expected/
7.1% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 3f4a27a736e..77b41e02b82 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..12ae9a8d321 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,23 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..d3491faaff2 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Xact statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--SywurcztURg2uk+W--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v3 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. A new function is called in AtEOXact_PgStat() to increment those
two new counters.
---
src/backend/utils/activity/pgstat_backend.c | 57 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
4 files changed, 69 insertions(+), 1 deletion(-)
78.9% src/backend/utils/activity/
11.2% src/include/utils/
9.8% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..bf164854c4b 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,20 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /* Count transaction commit or abort */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..9efc0e10ebf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
--
2.34.1
--u6+Li7FU79Mt/lVd
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v3-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v4 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..47ce61e5093 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -36,7 +36,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index b31f20d41bc..400a8c5d734 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -342,6 +342,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..0fdbaf79780 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -801,6 +809,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..b97184c6539 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,7 +616,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--jg9kqX3osLGO+8A7
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v4-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v5 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 7727fed3bda..42bfb9cd38f 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -37,7 +37,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -48,6 +48,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -261,6 +266,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -285,6 +318,10 @@ pgstat_flush_backend(bool nowait, uint32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -300,6 +337,9 @@ pgstat_flush_backend(bool nowait, uint32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index 933dcb5cae5..218ff5e653c 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -352,6 +352,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 8e3549c3752..5d73c4dfbcf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -523,6 +523,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -535,6 +537,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -844,6 +852,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index eed4c6b359c..8077c65e938 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -707,7 +707,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, uint32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--uN5gGi2rtCNSVbpC
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v5-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v1] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 2 +
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 160 insertions(+), 1 deletion(-)
26.9% doc/src/sgml/
29.1% src/backend/utils/activity/
12.3% src/backend/utils/adt/
9.7% src/include/catalog/
4.0% src/include/utils/
9.2% src/test/regress/expected/
7.3% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index fa78031ccbb..ef89683164f 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..7cec0c83071 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,13 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static int pgStatBackendXactCommit = 0;
+static int pgStatBackendXactRollback = 0;
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +266,33 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += pgStatBackendXactCommit;
+ shbackendent->stats.xact_rollback += pgStatBackendXactRollback;
+
+ pgStatBackendXactCommit = pgStatBackendXactRollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +317,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +336,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +441,22 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ pgStatBackendXactCommit++;
+ else
+ pgStatBackendXactRollback++;
+
+ backend_has_xactstats = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..4c9f6b0dcec 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--ZnbSmJtA6EgjuGCe--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v1] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 2 +
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 160 insertions(+), 1 deletion(-)
26.9% doc/src/sgml/
29.1% src/backend/utils/activity/
12.3% src/backend/utils/adt/
9.7% src/include/catalog/
4.0% src/include/utils/
9.2% src/test/regress/expected/
7.3% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index fa78031ccbb..ef89683164f 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..7cec0c83071 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,13 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static int pgStatBackendXactCommit = 0;
+static int pgStatBackendXactRollback = 0;
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +266,33 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += pgStatBackendXactCommit;
+ shbackendent->stats.xact_rollback += pgStatBackendXactRollback;
+
+ pgStatBackendXactCommit = pgStatBackendXactRollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +317,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +336,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +441,22 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ pgStatBackendXactCommit++;
+ else
+ pgStatBackendXactRollback++;
+
+ backend_has_xactstats = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..4c9f6b0dcec 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--ZnbSmJtA6EgjuGCe--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v5 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 7727fed3bda..42bfb9cd38f 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -37,7 +37,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -48,6 +48,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -261,6 +266,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -285,6 +318,10 @@ pgstat_flush_backend(bool nowait, uint32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -300,6 +337,9 @@ pgstat_flush_backend(bool nowait, uint32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index 933dcb5cae5..218ff5e653c 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -352,6 +352,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 8e3549c3752..5d73c4dfbcf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -523,6 +523,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -535,6 +537,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -844,6 +852,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index eed4c6b359c..8077c65e938 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -707,7 +707,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, uint32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--uN5gGi2rtCNSVbpC
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v5-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v1] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 2 +
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 160 insertions(+), 1 deletion(-)
26.9% doc/src/sgml/
29.1% src/backend/utils/activity/
12.3% src/backend/utils/adt/
9.7% src/include/catalog/
4.0% src/include/utils/
9.2% src/test/regress/expected/
7.3% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index fa78031ccbb..ef89683164f 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..7cec0c83071 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,13 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static int pgStatBackendXactCommit = 0;
+static int pgStatBackendXactRollback = 0;
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +266,33 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += pgStatBackendXactCommit;
+ shbackendent->stats.xact_rollback += pgStatBackendXactRollback;
+
+ pgStatBackendXactCommit = pgStatBackendXactRollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +317,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +336,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +441,22 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ pgStatBackendXactCommit++;
+ else
+ pgStatBackendXactRollback++;
+
+ backend_has_xactstats = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..4c9f6b0dcec 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--ZnbSmJtA6EgjuGCe--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v2] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 166 insertions(+), 1 deletion(-)
26.1% doc/src/sgml/
29.1% src/backend/utils/activity/
11.9% src/backend/utils/adt/
9.4% src/include/catalog/
3.9% src/include/utils/
3.3% src/include/
8.9% src/test/regress/expected/
7.1% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 3f4a27a736e..77b41e02b82 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..12ae9a8d321 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,23 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..d3491faaff2 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Xact statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--SywurcztURg2uk+W--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v3 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. A new function is called in AtEOXact_PgStat() to increment those
two new counters.
---
src/backend/utils/activity/pgstat_backend.c | 57 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
4 files changed, 69 insertions(+), 1 deletion(-)
78.9% src/backend/utils/activity/
11.2% src/include/utils/
9.8% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..bf164854c4b 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,20 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /* Count transaction commit or abort */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..9efc0e10ebf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
--
2.34.1
--u6+Li7FU79Mt/lVd
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v3-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v4 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..47ce61e5093 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -36,7 +36,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index b31f20d41bc..400a8c5d734 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -342,6 +342,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..0fdbaf79780 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -801,6 +809,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..b97184c6539 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,7 +616,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--jg9kqX3osLGO+8A7
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v4-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v5 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 7727fed3bda..42bfb9cd38f 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -37,7 +37,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -48,6 +48,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -261,6 +266,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -285,6 +318,10 @@ pgstat_flush_backend(bool nowait, uint32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -300,6 +337,9 @@ pgstat_flush_backend(bool nowait, uint32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index 933dcb5cae5..218ff5e653c 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -352,6 +352,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 8e3549c3752..5d73c4dfbcf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -523,6 +523,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -535,6 +537,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -844,6 +852,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index eed4c6b359c..8077c65e938 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -707,7 +707,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, uint32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--uN5gGi2rtCNSVbpC
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v5-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v1] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 2 +
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 160 insertions(+), 1 deletion(-)
26.9% doc/src/sgml/
29.1% src/backend/utils/activity/
12.3% src/backend/utils/adt/
9.7% src/include/catalog/
4.0% src/include/utils/
9.2% src/test/regress/expected/
7.3% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index fa78031ccbb..ef89683164f 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..7cec0c83071 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,13 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static int pgStatBackendXactCommit = 0;
+static int pgStatBackendXactRollback = 0;
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +266,33 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += pgStatBackendXactCommit;
+ shbackendent->stats.xact_rollback += pgStatBackendXactRollback;
+
+ pgStatBackendXactCommit = pgStatBackendXactRollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +317,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +336,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +441,22 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ pgStatBackendXactCommit++;
+ else
+ pgStatBackendXactRollback++;
+
+ backend_has_xactstats = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..4c9f6b0dcec 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--ZnbSmJtA6EgjuGCe--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v2] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 166 insertions(+), 1 deletion(-)
26.1% doc/src/sgml/
29.1% src/backend/utils/activity/
11.9% src/backend/utils/adt/
9.4% src/include/catalog/
3.9% src/include/utils/
3.3% src/include/
8.9% src/test/regress/expected/
7.1% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 3f4a27a736e..77b41e02b82 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..12ae9a8d321 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,23 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..d3491faaff2 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Xact statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--SywurcztURg2uk+W--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v3 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. A new function is called in AtEOXact_PgStat() to increment those
two new counters.
---
src/backend/utils/activity/pgstat_backend.c | 57 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
4 files changed, 69 insertions(+), 1 deletion(-)
78.9% src/backend/utils/activity/
11.2% src/include/utils/
9.8% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..bf164854c4b 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,20 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /* Count transaction commit or abort */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..9efc0e10ebf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
--
2.34.1
--u6+Li7FU79Mt/lVd
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v3-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v4 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..47ce61e5093 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -36,7 +36,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index b31f20d41bc..400a8c5d734 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -342,6 +342,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..0fdbaf79780 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -801,6 +809,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..b97184c6539 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,7 +616,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--jg9kqX3osLGO+8A7
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v4-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v5 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 7727fed3bda..42bfb9cd38f 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -37,7 +37,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -48,6 +48,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -261,6 +266,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -285,6 +318,10 @@ pgstat_flush_backend(bool nowait, uint32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -300,6 +337,9 @@ pgstat_flush_backend(bool nowait, uint32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index 933dcb5cae5..218ff5e653c 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -352,6 +352,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 8e3549c3752..5d73c4dfbcf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -523,6 +523,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -535,6 +537,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -844,6 +852,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index eed4c6b359c..8077c65e938 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -707,7 +707,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, uint32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--uN5gGi2rtCNSVbpC
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v5-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v1] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 2 +
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 160 insertions(+), 1 deletion(-)
26.9% doc/src/sgml/
29.1% src/backend/utils/activity/
12.3% src/backend/utils/adt/
9.7% src/include/catalog/
4.0% src/include/utils/
9.2% src/test/regress/expected/
7.3% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index fa78031ccbb..ef89683164f 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..7cec0c83071 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,13 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static int pgStatBackendXactCommit = 0;
+static int pgStatBackendXactRollback = 0;
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +266,33 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += pgStatBackendXactCommit;
+ shbackendent->stats.xact_rollback += pgStatBackendXactRollback;
+
+ pgStatBackendXactCommit = pgStatBackendXactRollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +317,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +336,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +441,22 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ pgStatBackendXactCommit++;
+ else
+ pgStatBackendXactRollback++;
+
+ backend_has_xactstats = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..4c9f6b0dcec 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--ZnbSmJtA6EgjuGCe--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v2] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 166 insertions(+), 1 deletion(-)
26.1% doc/src/sgml/
29.1% src/backend/utils/activity/
11.9% src/backend/utils/adt/
9.4% src/include/catalog/
3.9% src/include/utils/
3.3% src/include/
8.9% src/test/regress/expected/
7.1% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 3f4a27a736e..77b41e02b82 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..12ae9a8d321 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,23 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..d3491faaff2 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Xact statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--SywurcztURg2uk+W--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v3 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. A new function is called in AtEOXact_PgStat() to increment those
two new counters.
---
src/backend/utils/activity/pgstat_backend.c | 57 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
4 files changed, 69 insertions(+), 1 deletion(-)
78.9% src/backend/utils/activity/
11.2% src/include/utils/
9.8% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..bf164854c4b 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,20 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /* Count transaction commit or abort */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..9efc0e10ebf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
--
2.34.1
--u6+Li7FU79Mt/lVd
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v3-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v4 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..47ce61e5093 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -36,7 +36,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index b31f20d41bc..400a8c5d734 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -342,6 +342,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..0fdbaf79780 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -801,6 +809,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..b97184c6539 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,7 +616,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--jg9kqX3osLGO+8A7
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v4-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v5 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 7727fed3bda..42bfb9cd38f 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -37,7 +37,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -48,6 +48,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -261,6 +266,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -285,6 +318,10 @@ pgstat_flush_backend(bool nowait, uint32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -300,6 +337,9 @@ pgstat_flush_backend(bool nowait, uint32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index 933dcb5cae5..218ff5e653c 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -352,6 +352,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 8e3549c3752..5d73c4dfbcf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -523,6 +523,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -535,6 +537,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -844,6 +852,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index eed4c6b359c..8077c65e938 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -707,7 +707,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, uint32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--uN5gGi2rtCNSVbpC
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v5-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v1] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 2 +
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 160 insertions(+), 1 deletion(-)
26.9% doc/src/sgml/
29.1% src/backend/utils/activity/
12.3% src/backend/utils/adt/
9.7% src/include/catalog/
4.0% src/include/utils/
9.2% src/test/regress/expected/
7.3% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index fa78031ccbb..ef89683164f 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..7cec0c83071 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,13 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static int pgStatBackendXactCommit = 0;
+static int pgStatBackendXactRollback = 0;
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +266,33 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += pgStatBackendXactCommit;
+ shbackendent->stats.xact_rollback += pgStatBackendXactRollback;
+
+ pgStatBackendXactCommit = pgStatBackendXactRollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +317,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +336,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +441,22 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ pgStatBackendXactCommit++;
+ else
+ pgStatBackendXactRollback++;
+
+ backend_has_xactstats = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..4c9f6b0dcec 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--ZnbSmJtA6EgjuGCe--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v2] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 166 insertions(+), 1 deletion(-)
26.1% doc/src/sgml/
29.1% src/backend/utils/activity/
11.9% src/backend/utils/adt/
9.4% src/include/catalog/
3.9% src/include/utils/
3.3% src/include/
8.9% src/test/regress/expected/
7.1% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 3f4a27a736e..77b41e02b82 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..12ae9a8d321 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,23 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..d3491faaff2 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Xact statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--SywurcztURg2uk+W--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v3 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. A new function is called in AtEOXact_PgStat() to increment those
two new counters.
---
src/backend/utils/activity/pgstat_backend.c | 57 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
4 files changed, 69 insertions(+), 1 deletion(-)
78.9% src/backend/utils/activity/
11.2% src/include/utils/
9.8% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..bf164854c4b 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,20 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /* Count transaction commit or abort */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..9efc0e10ebf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
--
2.34.1
--u6+Li7FU79Mt/lVd
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v3-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v4 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..47ce61e5093 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -36,7 +36,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index b31f20d41bc..400a8c5d734 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -342,6 +342,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..0fdbaf79780 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -801,6 +809,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..b97184c6539 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,7 +616,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--jg9kqX3osLGO+8A7
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v4-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v5 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 7727fed3bda..42bfb9cd38f 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -37,7 +37,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -48,6 +48,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -261,6 +266,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -285,6 +318,10 @@ pgstat_flush_backend(bool nowait, uint32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -300,6 +337,9 @@ pgstat_flush_backend(bool nowait, uint32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index 933dcb5cae5..218ff5e653c 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -352,6 +352,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 8e3549c3752..5d73c4dfbcf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -523,6 +523,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -535,6 +537,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -844,6 +852,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index eed4c6b359c..8077c65e938 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -707,7 +707,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, uint32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--uN5gGi2rtCNSVbpC
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v5-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v1] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 2 +
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 160 insertions(+), 1 deletion(-)
26.9% doc/src/sgml/
29.1% src/backend/utils/activity/
12.3% src/backend/utils/adt/
9.7% src/include/catalog/
4.0% src/include/utils/
9.2% src/test/regress/expected/
7.3% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index fa78031ccbb..ef89683164f 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..7cec0c83071 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,13 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static int pgStatBackendXactCommit = 0;
+static int pgStatBackendXactRollback = 0;
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +266,33 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += pgStatBackendXactCommit;
+ shbackendent->stats.xact_rollback += pgStatBackendXactRollback;
+
+ pgStatBackendXactCommit = pgStatBackendXactRollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +317,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +336,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +441,22 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ pgStatBackendXactCommit++;
+ else
+ pgStatBackendXactRollback++;
+
+ backend_has_xactstats = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..4c9f6b0dcec 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--ZnbSmJtA6EgjuGCe--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v2] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 166 insertions(+), 1 deletion(-)
26.1% doc/src/sgml/
29.1% src/backend/utils/activity/
11.9% src/backend/utils/adt/
9.4% src/include/catalog/
3.9% src/include/utils/
3.3% src/include/
8.9% src/test/regress/expected/
7.1% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 3f4a27a736e..77b41e02b82 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..12ae9a8d321 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,23 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..d3491faaff2 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Xact statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--SywurcztURg2uk+W--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v3 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. A new function is called in AtEOXact_PgStat() to increment those
two new counters.
---
src/backend/utils/activity/pgstat_backend.c | 57 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
4 files changed, 69 insertions(+), 1 deletion(-)
78.9% src/backend/utils/activity/
11.2% src/include/utils/
9.8% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..bf164854c4b 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,20 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /* Count transaction commit or abort */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..9efc0e10ebf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
--
2.34.1
--u6+Li7FU79Mt/lVd
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v3-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v4 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..47ce61e5093 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -36,7 +36,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index b31f20d41bc..400a8c5d734 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -342,6 +342,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..0fdbaf79780 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -801,6 +809,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..b97184c6539 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,7 +616,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--jg9kqX3osLGO+8A7
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v4-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v5 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 7727fed3bda..42bfb9cd38f 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -37,7 +37,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -48,6 +48,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -261,6 +266,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -285,6 +318,10 @@ pgstat_flush_backend(bool nowait, uint32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -300,6 +337,9 @@ pgstat_flush_backend(bool nowait, uint32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index 933dcb5cae5..218ff5e653c 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -352,6 +352,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 8e3549c3752..5d73c4dfbcf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -523,6 +523,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -535,6 +537,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -844,6 +852,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index eed4c6b359c..8077c65e938 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -707,7 +707,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, uint32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--uN5gGi2rtCNSVbpC
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v5-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v1] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 2 +
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 160 insertions(+), 1 deletion(-)
26.9% doc/src/sgml/
29.1% src/backend/utils/activity/
12.3% src/backend/utils/adt/
9.7% src/include/catalog/
4.0% src/include/utils/
9.2% src/test/regress/expected/
7.3% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index fa78031ccbb..ef89683164f 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..7cec0c83071 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,13 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static int pgStatBackendXactCommit = 0;
+static int pgStatBackendXactRollback = 0;
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +266,33 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += pgStatBackendXactCommit;
+ shbackendent->stats.xact_rollback += pgStatBackendXactRollback;
+
+ pgStatBackendXactCommit = pgStatBackendXactRollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +317,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +336,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +441,22 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ pgStatBackendXactCommit++;
+ else
+ pgStatBackendXactRollback++;
+
+ backend_has_xactstats = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..4c9f6b0dcec 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--ZnbSmJtA6EgjuGCe--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v2] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 166 insertions(+), 1 deletion(-)
26.1% doc/src/sgml/
29.1% src/backend/utils/activity/
11.9% src/backend/utils/adt/
9.4% src/include/catalog/
3.9% src/include/utils/
3.3% src/include/
8.9% src/test/regress/expected/
7.1% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 3f4a27a736e..77b41e02b82 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..12ae9a8d321 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,23 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..d3491faaff2 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Xact statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--SywurcztURg2uk+W--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v3 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. A new function is called in AtEOXact_PgStat() to increment those
two new counters.
---
src/backend/utils/activity/pgstat_backend.c | 57 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
4 files changed, 69 insertions(+), 1 deletion(-)
78.9% src/backend/utils/activity/
11.2% src/include/utils/
9.8% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..bf164854c4b 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,20 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /* Count transaction commit or abort */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..9efc0e10ebf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
--
2.34.1
--u6+Li7FU79Mt/lVd
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v3-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v4 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..47ce61e5093 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -36,7 +36,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index b31f20d41bc..400a8c5d734 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -342,6 +342,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..0fdbaf79780 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -801,6 +809,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..b97184c6539 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,7 +616,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--jg9kqX3osLGO+8A7
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v4-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v5 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 7727fed3bda..42bfb9cd38f 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -37,7 +37,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -48,6 +48,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -261,6 +266,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -285,6 +318,10 @@ pgstat_flush_backend(bool nowait, uint32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -300,6 +337,9 @@ pgstat_flush_backend(bool nowait, uint32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index 933dcb5cae5..218ff5e653c 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -352,6 +352,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 8e3549c3752..5d73c4dfbcf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -523,6 +523,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -535,6 +537,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -844,6 +852,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index eed4c6b359c..8077c65e938 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -707,7 +707,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, uint32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--uN5gGi2rtCNSVbpC
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v5-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v4 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..47ce61e5093 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -36,7 +36,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index b31f20d41bc..400a8c5d734 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -342,6 +342,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..0fdbaf79780 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -801,6 +809,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..b97184c6539 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,7 +616,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--jg9kqX3osLGO+8A7
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v4-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v5 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 7727fed3bda..42bfb9cd38f 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -37,7 +37,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -48,6 +48,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -261,6 +266,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -285,6 +318,10 @@ pgstat_flush_backend(bool nowait, uint32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -300,6 +337,9 @@ pgstat_flush_backend(bool nowait, uint32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index 933dcb5cae5..218ff5e653c 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -352,6 +352,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 8e3549c3752..5d73c4dfbcf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -523,6 +523,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -535,6 +537,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -844,6 +852,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index eed4c6b359c..8077c65e938 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -707,7 +707,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, uint32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--uN5gGi2rtCNSVbpC
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v5-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v1] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 2 +
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 160 insertions(+), 1 deletion(-)
26.9% doc/src/sgml/
29.1% src/backend/utils/activity/
12.3% src/backend/utils/adt/
9.7% src/include/catalog/
4.0% src/include/utils/
9.2% src/test/regress/expected/
7.3% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index fa78031ccbb..ef89683164f 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..7cec0c83071 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,13 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static int pgStatBackendXactCommit = 0;
+static int pgStatBackendXactRollback = 0;
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +266,33 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += pgStatBackendXactCommit;
+ shbackendent->stats.xact_rollback += pgStatBackendXactRollback;
+
+ pgStatBackendXactCommit = pgStatBackendXactRollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +317,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +336,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +441,22 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ pgStatBackendXactCommit++;
+ else
+ pgStatBackendXactRollback++;
+
+ backend_has_xactstats = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..4c9f6b0dcec 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--ZnbSmJtA6EgjuGCe--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v2] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 166 insertions(+), 1 deletion(-)
26.1% doc/src/sgml/
29.1% src/backend/utils/activity/
11.9% src/backend/utils/adt/
9.4% src/include/catalog/
3.9% src/include/utils/
3.3% src/include/
8.9% src/test/regress/expected/
7.1% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 3f4a27a736e..77b41e02b82 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..12ae9a8d321 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,23 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..d3491faaff2 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Xact statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--SywurcztURg2uk+W--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v3 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. A new function is called in AtEOXact_PgStat() to increment those
two new counters.
---
src/backend/utils/activity/pgstat_backend.c | 57 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
4 files changed, 69 insertions(+), 1 deletion(-)
78.9% src/backend/utils/activity/
11.2% src/include/utils/
9.8% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..bf164854c4b 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,20 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /* Count transaction commit or abort */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..9efc0e10ebf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
--
2.34.1
--u6+Li7FU79Mt/lVd
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v3-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v4 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..47ce61e5093 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -36,7 +36,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index b31f20d41bc..400a8c5d734 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -342,6 +342,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..0fdbaf79780 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -801,6 +809,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..b97184c6539 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,7 +616,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--jg9kqX3osLGO+8A7
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v4-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v5 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 7727fed3bda..42bfb9cd38f 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -37,7 +37,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -48,6 +48,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -261,6 +266,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -285,6 +318,10 @@ pgstat_flush_backend(bool nowait, uint32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -300,6 +337,9 @@ pgstat_flush_backend(bool nowait, uint32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index 933dcb5cae5..218ff5e653c 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -352,6 +352,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 8e3549c3752..5d73c4dfbcf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -523,6 +523,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -535,6 +537,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -844,6 +852,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index eed4c6b359c..8077c65e938 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -707,7 +707,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, uint32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--uN5gGi2rtCNSVbpC
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v5-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v1] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 2 +
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 160 insertions(+), 1 deletion(-)
26.9% doc/src/sgml/
29.1% src/backend/utils/activity/
12.3% src/backend/utils/adt/
9.7% src/include/catalog/
4.0% src/include/utils/
9.2% src/test/regress/expected/
7.3% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index fa78031ccbb..ef89683164f 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..7cec0c83071 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,13 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static int pgStatBackendXactCommit = 0;
+static int pgStatBackendXactRollback = 0;
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +266,33 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += pgStatBackendXactCommit;
+ shbackendent->stats.xact_rollback += pgStatBackendXactRollback;
+
+ pgStatBackendXactCommit = pgStatBackendXactRollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +317,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +336,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +441,22 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ pgStatBackendXactCommit++;
+ else
+ pgStatBackendXactRollback++;
+
+ backend_has_xactstats = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..4c9f6b0dcec 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--ZnbSmJtA6EgjuGCe--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v2] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 166 insertions(+), 1 deletion(-)
26.1% doc/src/sgml/
29.1% src/backend/utils/activity/
11.9% src/backend/utils/adt/
9.4% src/include/catalog/
3.9% src/include/utils/
3.3% src/include/
8.9% src/test/regress/expected/
7.1% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 3f4a27a736e..77b41e02b82 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..12ae9a8d321 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,23 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..d3491faaff2 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Xact statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--SywurcztURg2uk+W--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v3 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. A new function is called in AtEOXact_PgStat() to increment those
two new counters.
---
src/backend/utils/activity/pgstat_backend.c | 57 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
4 files changed, 69 insertions(+), 1 deletion(-)
78.9% src/backend/utils/activity/
11.2% src/include/utils/
9.8% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..bf164854c4b 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,20 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /* Count transaction commit or abort */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..9efc0e10ebf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
--
2.34.1
--u6+Li7FU79Mt/lVd
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v3-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v4 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..47ce61e5093 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -36,7 +36,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index b31f20d41bc..400a8c5d734 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -342,6 +342,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..0fdbaf79780 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -801,6 +809,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..b97184c6539 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,7 +616,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--jg9kqX3osLGO+8A7
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v4-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v5 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 7727fed3bda..42bfb9cd38f 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -37,7 +37,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -48,6 +48,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -261,6 +266,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -285,6 +318,10 @@ pgstat_flush_backend(bool nowait, uint32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -300,6 +337,9 @@ pgstat_flush_backend(bool nowait, uint32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index 933dcb5cae5..218ff5e653c 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -352,6 +352,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 8e3549c3752..5d73c4dfbcf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -523,6 +523,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -535,6 +537,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -844,6 +852,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index eed4c6b359c..8077c65e938 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -707,7 +707,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, uint32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--uN5gGi2rtCNSVbpC
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v5-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v1] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 2 +
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 160 insertions(+), 1 deletion(-)
26.9% doc/src/sgml/
29.1% src/backend/utils/activity/
12.3% src/backend/utils/adt/
9.7% src/include/catalog/
4.0% src/include/utils/
9.2% src/test/regress/expected/
7.3% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index fa78031ccbb..ef89683164f 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..7cec0c83071 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,13 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static int pgStatBackendXactCommit = 0;
+static int pgStatBackendXactRollback = 0;
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +266,33 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += pgStatBackendXactCommit;
+ shbackendent->stats.xact_rollback += pgStatBackendXactRollback;
+
+ pgStatBackendXactCommit = pgStatBackendXactRollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +317,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +336,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +441,22 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ pgStatBackendXactCommit++;
+ else
+ pgStatBackendXactRollback++;
+
+ backend_has_xactstats = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..4c9f6b0dcec 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--ZnbSmJtA6EgjuGCe--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v2] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 166 insertions(+), 1 deletion(-)
26.1% doc/src/sgml/
29.1% src/backend/utils/activity/
11.9% src/backend/utils/adt/
9.4% src/include/catalog/
3.9% src/include/utils/
3.3% src/include/
8.9% src/test/regress/expected/
7.1% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 3f4a27a736e..77b41e02b82 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..12ae9a8d321 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,23 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..d3491faaff2 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Xact statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--SywurcztURg2uk+W--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v3 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. A new function is called in AtEOXact_PgStat() to increment those
two new counters.
---
src/backend/utils/activity/pgstat_backend.c | 57 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
4 files changed, 69 insertions(+), 1 deletion(-)
78.9% src/backend/utils/activity/
11.2% src/include/utils/
9.8% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..bf164854c4b 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,20 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /* Count transaction commit or abort */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..9efc0e10ebf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
--
2.34.1
--u6+Li7FU79Mt/lVd
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v3-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v4 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..47ce61e5093 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -36,7 +36,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index b31f20d41bc..400a8c5d734 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -342,6 +342,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..0fdbaf79780 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -801,6 +809,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..b97184c6539 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,7 +616,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--jg9kqX3osLGO+8A7
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v4-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v5 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 7727fed3bda..42bfb9cd38f 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -37,7 +37,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -48,6 +48,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -261,6 +266,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -285,6 +318,10 @@ pgstat_flush_backend(bool nowait, uint32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -300,6 +337,9 @@ pgstat_flush_backend(bool nowait, uint32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index 933dcb5cae5..218ff5e653c 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -352,6 +352,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 8e3549c3752..5d73c4dfbcf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -523,6 +523,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -535,6 +537,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -844,6 +852,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index eed4c6b359c..8077c65e938 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -707,7 +707,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, uint32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--uN5gGi2rtCNSVbpC
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v5-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v3 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. A new function is called in AtEOXact_PgStat() to increment those
two new counters.
---
src/backend/utils/activity/pgstat_backend.c | 57 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
4 files changed, 69 insertions(+), 1 deletion(-)
78.9% src/backend/utils/activity/
11.2% src/include/utils/
9.8% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..bf164854c4b 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,20 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /* Count transaction commit or abort */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..9efc0e10ebf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
--
2.34.1
--u6+Li7FU79Mt/lVd
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v3-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v1] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 2 +
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 160 insertions(+), 1 deletion(-)
26.9% doc/src/sgml/
29.1% src/backend/utils/activity/
12.3% src/backend/utils/adt/
9.7% src/include/catalog/
4.0% src/include/utils/
9.2% src/test/regress/expected/
7.3% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index fa78031ccbb..ef89683164f 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..7cec0c83071 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,13 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static int pgStatBackendXactCommit = 0;
+static int pgStatBackendXactRollback = 0;
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +266,33 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += pgStatBackendXactCommit;
+ shbackendent->stats.xact_rollback += pgStatBackendXactRollback;
+
+ pgStatBackendXactCommit = pgStatBackendXactRollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +317,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +336,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +441,22 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ pgStatBackendXactCommit++;
+ else
+ pgStatBackendXactRollback++;
+
+ backend_has_xactstats = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..4c9f6b0dcec 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--ZnbSmJtA6EgjuGCe--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v2] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 166 insertions(+), 1 deletion(-)
26.1% doc/src/sgml/
29.1% src/backend/utils/activity/
11.9% src/backend/utils/adt/
9.4% src/include/catalog/
3.9% src/include/utils/
3.3% src/include/
8.9% src/test/regress/expected/
7.1% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 3f4a27a736e..77b41e02b82 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..12ae9a8d321 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,23 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..d3491faaff2 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Xact statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--SywurcztURg2uk+W--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v3 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. A new function is called in AtEOXact_PgStat() to increment those
two new counters.
---
src/backend/utils/activity/pgstat_backend.c | 57 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
4 files changed, 69 insertions(+), 1 deletion(-)
78.9% src/backend/utils/activity/
11.2% src/include/utils/
9.8% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..bf164854c4b 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,20 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /* Count transaction commit or abort */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..9efc0e10ebf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
--
2.34.1
--u6+Li7FU79Mt/lVd
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v3-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v4 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..47ce61e5093 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -36,7 +36,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index b31f20d41bc..400a8c5d734 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -342,6 +342,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..0fdbaf79780 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -801,6 +809,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..b97184c6539 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,7 +616,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--jg9kqX3osLGO+8A7
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v4-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v5 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 7727fed3bda..42bfb9cd38f 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -37,7 +37,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -48,6 +48,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -261,6 +266,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -285,6 +318,10 @@ pgstat_flush_backend(bool nowait, uint32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -300,6 +337,9 @@ pgstat_flush_backend(bool nowait, uint32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index 933dcb5cae5..218ff5e653c 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -352,6 +352,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 8e3549c3752..5d73c4dfbcf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -523,6 +523,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -535,6 +537,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -844,6 +852,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index eed4c6b359c..8077c65e938 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -707,7 +707,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, uint32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--uN5gGi2rtCNSVbpC
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v5-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v1] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 2 +
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 160 insertions(+), 1 deletion(-)
26.9% doc/src/sgml/
29.1% src/backend/utils/activity/
12.3% src/backend/utils/adt/
9.7% src/include/catalog/
4.0% src/include/utils/
9.2% src/test/regress/expected/
7.3% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index fa78031ccbb..ef89683164f 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..7cec0c83071 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,13 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static int pgStatBackendXactCommit = 0;
+static int pgStatBackendXactRollback = 0;
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +266,33 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += pgStatBackendXactCommit;
+ shbackendent->stats.xact_rollback += pgStatBackendXactRollback;
+
+ pgStatBackendXactCommit = pgStatBackendXactRollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +317,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +336,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +441,22 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ pgStatBackendXactCommit++;
+ else
+ pgStatBackendXactRollback++;
+
+ backend_has_xactstats = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..4c9f6b0dcec 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--ZnbSmJtA6EgjuGCe--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v2] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 166 insertions(+), 1 deletion(-)
26.1% doc/src/sgml/
29.1% src/backend/utils/activity/
11.9% src/backend/utils/adt/
9.4% src/include/catalog/
3.9% src/include/utils/
3.3% src/include/
8.9% src/test/regress/expected/
7.1% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 3f4a27a736e..77b41e02b82 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..12ae9a8d321 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,23 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..d3491faaff2 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Xact statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--SywurcztURg2uk+W--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v3 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. A new function is called in AtEOXact_PgStat() to increment those
two new counters.
---
src/backend/utils/activity/pgstat_backend.c | 57 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
4 files changed, 69 insertions(+), 1 deletion(-)
78.9% src/backend/utils/activity/
11.2% src/include/utils/
9.8% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..bf164854c4b 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,20 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /* Count transaction commit or abort */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..9efc0e10ebf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
--
2.34.1
--u6+Li7FU79Mt/lVd
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v3-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v4 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..47ce61e5093 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -36,7 +36,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index b31f20d41bc..400a8c5d734 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -342,6 +342,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..0fdbaf79780 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -801,6 +809,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..b97184c6539 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,7 +616,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--jg9kqX3osLGO+8A7
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v4-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v5 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 7727fed3bda..42bfb9cd38f 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -37,7 +37,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -48,6 +48,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -261,6 +266,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -285,6 +318,10 @@ pgstat_flush_backend(bool nowait, uint32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -300,6 +337,9 @@ pgstat_flush_backend(bool nowait, uint32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index 933dcb5cae5..218ff5e653c 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -352,6 +352,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 8e3549c3752..5d73c4dfbcf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -523,6 +523,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -535,6 +537,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -844,6 +852,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index eed4c6b359c..8077c65e938 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -707,7 +707,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, uint32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--uN5gGi2rtCNSVbpC
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v5-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v1] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 2 +
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 160 insertions(+), 1 deletion(-)
26.9% doc/src/sgml/
29.1% src/backend/utils/activity/
12.3% src/backend/utils/adt/
9.7% src/include/catalog/
4.0% src/include/utils/
9.2% src/test/regress/expected/
7.3% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index fa78031ccbb..ef89683164f 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..7cec0c83071 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,13 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static int pgStatBackendXactCommit = 0;
+static int pgStatBackendXactRollback = 0;
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +266,33 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += pgStatBackendXactCommit;
+ shbackendent->stats.xact_rollback += pgStatBackendXactRollback;
+
+ pgStatBackendXactCommit = pgStatBackendXactRollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +317,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +336,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +441,22 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ pgStatBackendXactCommit++;
+ else
+ pgStatBackendXactRollback++;
+
+ backend_has_xactstats = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..4c9f6b0dcec 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--ZnbSmJtA6EgjuGCe--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v2] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 166 insertions(+), 1 deletion(-)
26.1% doc/src/sgml/
29.1% src/backend/utils/activity/
11.9% src/backend/utils/adt/
9.4% src/include/catalog/
3.9% src/include/utils/
3.3% src/include/
8.9% src/test/regress/expected/
7.1% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 3f4a27a736e..77b41e02b82 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..12ae9a8d321 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,23 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..d3491faaff2 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Xact statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--SywurcztURg2uk+W--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v3 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. A new function is called in AtEOXact_PgStat() to increment those
two new counters.
---
src/backend/utils/activity/pgstat_backend.c | 57 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
4 files changed, 69 insertions(+), 1 deletion(-)
78.9% src/backend/utils/activity/
11.2% src/include/utils/
9.8% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..bf164854c4b 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,20 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /* Count transaction commit or abort */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..9efc0e10ebf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
--
2.34.1
--u6+Li7FU79Mt/lVd
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v3-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v4 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..47ce61e5093 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -36,7 +36,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index b31f20d41bc..400a8c5d734 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -342,6 +342,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..0fdbaf79780 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -801,6 +809,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..b97184c6539 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,7 +616,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--jg9kqX3osLGO+8A7
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v4-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v5 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 7727fed3bda..42bfb9cd38f 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -37,7 +37,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -48,6 +48,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -261,6 +266,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -285,6 +318,10 @@ pgstat_flush_backend(bool nowait, uint32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -300,6 +337,9 @@ pgstat_flush_backend(bool nowait, uint32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index 933dcb5cae5..218ff5e653c 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -352,6 +352,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 8e3549c3752..5d73c4dfbcf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -523,6 +523,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -535,6 +537,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -844,6 +852,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index eed4c6b359c..8077c65e938 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -707,7 +707,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, uint32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--uN5gGi2rtCNSVbpC
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v5-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v1] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 2 +
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 160 insertions(+), 1 deletion(-)
26.9% doc/src/sgml/
29.1% src/backend/utils/activity/
12.3% src/backend/utils/adt/
9.7% src/include/catalog/
4.0% src/include/utils/
9.2% src/test/regress/expected/
7.3% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index fa78031ccbb..ef89683164f 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..7cec0c83071 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,13 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static int pgStatBackendXactCommit = 0;
+static int pgStatBackendXactRollback = 0;
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +266,33 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += pgStatBackendXactCommit;
+ shbackendent->stats.xact_rollback += pgStatBackendXactRollback;
+
+ pgStatBackendXactCommit = pgStatBackendXactRollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +317,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +336,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +441,22 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ pgStatBackendXactCommit++;
+ else
+ pgStatBackendXactRollback++;
+
+ backend_has_xactstats = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..4c9f6b0dcec 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--ZnbSmJtA6EgjuGCe--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v2] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 166 insertions(+), 1 deletion(-)
26.1% doc/src/sgml/
29.1% src/backend/utils/activity/
11.9% src/backend/utils/adt/
9.4% src/include/catalog/
3.9% src/include/utils/
3.3% src/include/
8.9% src/test/regress/expected/
7.1% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 3f4a27a736e..77b41e02b82 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..12ae9a8d321 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,23 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..d3491faaff2 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Xact statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--SywurcztURg2uk+W--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v3 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. A new function is called in AtEOXact_PgStat() to increment those
two new counters.
---
src/backend/utils/activity/pgstat_backend.c | 57 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
4 files changed, 69 insertions(+), 1 deletion(-)
78.9% src/backend/utils/activity/
11.2% src/include/utils/
9.8% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..bf164854c4b 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,20 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /* Count transaction commit or abort */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..9efc0e10ebf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
--
2.34.1
--u6+Li7FU79Mt/lVd
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v3-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v4 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..47ce61e5093 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -36,7 +36,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index b31f20d41bc..400a8c5d734 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -342,6 +342,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..0fdbaf79780 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -801,6 +809,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..b97184c6539 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,7 +616,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--jg9kqX3osLGO+8A7
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v4-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v5 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 7727fed3bda..42bfb9cd38f 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -37,7 +37,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -48,6 +48,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -261,6 +266,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -285,6 +318,10 @@ pgstat_flush_backend(bool nowait, uint32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -300,6 +337,9 @@ pgstat_flush_backend(bool nowait, uint32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index 933dcb5cae5..218ff5e653c 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -352,6 +352,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 8e3549c3752..5d73c4dfbcf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -523,6 +523,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -535,6 +537,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -844,6 +852,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index eed4c6b359c..8077c65e938 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -707,7 +707,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, uint32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--uN5gGi2rtCNSVbpC
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v5-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v1] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 2 +
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 160 insertions(+), 1 deletion(-)
26.9% doc/src/sgml/
29.1% src/backend/utils/activity/
12.3% src/backend/utils/adt/
9.7% src/include/catalog/
4.0% src/include/utils/
9.2% src/test/regress/expected/
7.3% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index fa78031ccbb..ef89683164f 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..7cec0c83071 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,13 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static int pgStatBackendXactCommit = 0;
+static int pgStatBackendXactRollback = 0;
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +266,33 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += pgStatBackendXactCommit;
+ shbackendent->stats.xact_rollback += pgStatBackendXactRollback;
+
+ pgStatBackendXactCommit = pgStatBackendXactRollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +317,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +336,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +441,22 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ pgStatBackendXactCommit++;
+ else
+ pgStatBackendXactRollback++;
+
+ backend_has_xactstats = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..4c9f6b0dcec 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--ZnbSmJtA6EgjuGCe--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v2] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 166 insertions(+), 1 deletion(-)
26.1% doc/src/sgml/
29.1% src/backend/utils/activity/
11.9% src/backend/utils/adt/
9.4% src/include/catalog/
3.9% src/include/utils/
3.3% src/include/
8.9% src/test/regress/expected/
7.1% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 3f4a27a736e..77b41e02b82 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..12ae9a8d321 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,23 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..d3491faaff2 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Xact statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--SywurcztURg2uk+W--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v3 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. A new function is called in AtEOXact_PgStat() to increment those
two new counters.
---
src/backend/utils/activity/pgstat_backend.c | 57 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
4 files changed, 69 insertions(+), 1 deletion(-)
78.9% src/backend/utils/activity/
11.2% src/include/utils/
9.8% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..bf164854c4b 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,20 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /* Count transaction commit or abort */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..9efc0e10ebf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
--
2.34.1
--u6+Li7FU79Mt/lVd
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v3-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v4 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..47ce61e5093 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -36,7 +36,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index b31f20d41bc..400a8c5d734 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -342,6 +342,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..0fdbaf79780 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -801,6 +809,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..b97184c6539 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,7 +616,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--jg9kqX3osLGO+8A7
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v4-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v5 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 7727fed3bda..42bfb9cd38f 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -37,7 +37,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -48,6 +48,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -261,6 +266,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -285,6 +318,10 @@ pgstat_flush_backend(bool nowait, uint32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -300,6 +337,9 @@ pgstat_flush_backend(bool nowait, uint32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index 933dcb5cae5..218ff5e653c 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -352,6 +352,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 8e3549c3752..5d73c4dfbcf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -523,6 +523,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -535,6 +537,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -844,6 +852,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index eed4c6b359c..8077c65e938 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -707,7 +707,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, uint32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--uN5gGi2rtCNSVbpC
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v5-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v1] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 2 +
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 160 insertions(+), 1 deletion(-)
26.9% doc/src/sgml/
29.1% src/backend/utils/activity/
12.3% src/backend/utils/adt/
9.7% src/include/catalog/
4.0% src/include/utils/
9.2% src/test/regress/expected/
7.3% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index fa78031ccbb..ef89683164f 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..7cec0c83071 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,13 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static int pgStatBackendXactCommit = 0;
+static int pgStatBackendXactRollback = 0;
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +266,33 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += pgStatBackendXactCommit;
+ shbackendent->stats.xact_rollback += pgStatBackendXactRollback;
+
+ pgStatBackendXactCommit = pgStatBackendXactRollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +317,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +336,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +441,22 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ pgStatBackendXactCommit++;
+ else
+ pgStatBackendXactRollback++;
+
+ backend_has_xactstats = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..4c9f6b0dcec 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--ZnbSmJtA6EgjuGCe--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v2] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 166 insertions(+), 1 deletion(-)
26.1% doc/src/sgml/
29.1% src/backend/utils/activity/
11.9% src/backend/utils/adt/
9.4% src/include/catalog/
3.9% src/include/utils/
3.3% src/include/
8.9% src/test/regress/expected/
7.1% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 3f4a27a736e..77b41e02b82 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..12ae9a8d321 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,23 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..d3491faaff2 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Xact statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--SywurcztURg2uk+W--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v3 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. A new function is called in AtEOXact_PgStat() to increment those
two new counters.
---
src/backend/utils/activity/pgstat_backend.c | 57 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
4 files changed, 69 insertions(+), 1 deletion(-)
78.9% src/backend/utils/activity/
11.2% src/include/utils/
9.8% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..bf164854c4b 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,20 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /* Count transaction commit or abort */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..9efc0e10ebf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
--
2.34.1
--u6+Li7FU79Mt/lVd
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v3-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v4 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..47ce61e5093 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -36,7 +36,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index b31f20d41bc..400a8c5d734 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -342,6 +342,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..0fdbaf79780 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -801,6 +809,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..b97184c6539 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,7 +616,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--jg9kqX3osLGO+8A7
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v4-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v5 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 7727fed3bda..42bfb9cd38f 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -37,7 +37,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -48,6 +48,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -261,6 +266,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -285,6 +318,10 @@ pgstat_flush_backend(bool nowait, uint32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -300,6 +337,9 @@ pgstat_flush_backend(bool nowait, uint32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index 933dcb5cae5..218ff5e653c 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -352,6 +352,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 8e3549c3752..5d73c4dfbcf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -523,6 +523,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -535,6 +537,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -844,6 +852,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index eed4c6b359c..8077c65e938 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -707,7 +707,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, uint32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--uN5gGi2rtCNSVbpC
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v5-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v1] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 2 +
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 160 insertions(+), 1 deletion(-)
26.9% doc/src/sgml/
29.1% src/backend/utils/activity/
12.3% src/backend/utils/adt/
9.7% src/include/catalog/
4.0% src/include/utils/
9.2% src/test/regress/expected/
7.3% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index fa78031ccbb..ef89683164f 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..7cec0c83071 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,13 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static int pgStatBackendXactCommit = 0;
+static int pgStatBackendXactRollback = 0;
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +266,33 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += pgStatBackendXactCommit;
+ shbackendent->stats.xact_rollback += pgStatBackendXactRollback;
+
+ pgStatBackendXactCommit = pgStatBackendXactRollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +317,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +336,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +441,22 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ pgStatBackendXactCommit++;
+ else
+ pgStatBackendXactRollback++;
+
+ backend_has_xactstats = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..4c9f6b0dcec 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--ZnbSmJtA6EgjuGCe--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v2] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 166 insertions(+), 1 deletion(-)
26.1% doc/src/sgml/
29.1% src/backend/utils/activity/
11.9% src/backend/utils/adt/
9.4% src/include/catalog/
3.9% src/include/utils/
3.3% src/include/
8.9% src/test/regress/expected/
7.1% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 3f4a27a736e..77b41e02b82 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..12ae9a8d321 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,23 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..d3491faaff2 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Xact statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--SywurcztURg2uk+W--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v3 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. A new function is called in AtEOXact_PgStat() to increment those
two new counters.
---
src/backend/utils/activity/pgstat_backend.c | 57 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
4 files changed, 69 insertions(+), 1 deletion(-)
78.9% src/backend/utils/activity/
11.2% src/include/utils/
9.8% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..bf164854c4b 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,20 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /* Count transaction commit or abort */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..9efc0e10ebf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
--
2.34.1
--u6+Li7FU79Mt/lVd
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v3-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v4 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..47ce61e5093 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -36,7 +36,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index b31f20d41bc..400a8c5d734 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -342,6 +342,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..0fdbaf79780 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -801,6 +809,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..b97184c6539 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,7 +616,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--jg9kqX3osLGO+8A7
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v4-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v5 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 7727fed3bda..42bfb9cd38f 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -37,7 +37,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -48,6 +48,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -261,6 +266,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -285,6 +318,10 @@ pgstat_flush_backend(bool nowait, uint32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -300,6 +337,9 @@ pgstat_flush_backend(bool nowait, uint32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index 933dcb5cae5..218ff5e653c 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -352,6 +352,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 8e3549c3752..5d73c4dfbcf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -523,6 +523,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -535,6 +537,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -844,6 +852,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index eed4c6b359c..8077c65e938 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -707,7 +707,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, uint32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--uN5gGi2rtCNSVbpC
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v5-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v3 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. A new function is called in AtEOXact_PgStat() to increment those
two new counters.
---
src/backend/utils/activity/pgstat_backend.c | 57 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
4 files changed, 69 insertions(+), 1 deletion(-)
78.9% src/backend/utils/activity/
11.2% src/include/utils/
9.8% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..bf164854c4b 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,20 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /* Count transaction commit or abort */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..9efc0e10ebf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
--
2.34.1
--u6+Li7FU79Mt/lVd
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v3-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v1] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 2 +
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 160 insertions(+), 1 deletion(-)
26.9% doc/src/sgml/
29.1% src/backend/utils/activity/
12.3% src/backend/utils/adt/
9.7% src/include/catalog/
4.0% src/include/utils/
9.2% src/test/regress/expected/
7.3% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index fa78031ccbb..ef89683164f 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..7cec0c83071 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,13 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static int pgStatBackendXactCommit = 0;
+static int pgStatBackendXactRollback = 0;
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +266,33 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += pgStatBackendXactCommit;
+ shbackendent->stats.xact_rollback += pgStatBackendXactRollback;
+
+ pgStatBackendXactCommit = pgStatBackendXactRollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +317,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +336,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +441,22 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ pgStatBackendXactCommit++;
+ else
+ pgStatBackendXactRollback++;
+
+ backend_has_xactstats = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..4c9f6b0dcec 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--ZnbSmJtA6EgjuGCe--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v2] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 166 insertions(+), 1 deletion(-)
26.1% doc/src/sgml/
29.1% src/backend/utils/activity/
11.9% src/backend/utils/adt/
9.4% src/include/catalog/
3.9% src/include/utils/
3.3% src/include/
8.9% src/test/regress/expected/
7.1% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 3f4a27a736e..77b41e02b82 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..12ae9a8d321 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,23 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..d3491faaff2 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Xact statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--SywurcztURg2uk+W--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v3 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. A new function is called in AtEOXact_PgStat() to increment those
two new counters.
---
src/backend/utils/activity/pgstat_backend.c | 57 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
4 files changed, 69 insertions(+), 1 deletion(-)
78.9% src/backend/utils/activity/
11.2% src/include/utils/
9.8% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..bf164854c4b 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,20 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /* Count transaction commit or abort */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..9efc0e10ebf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
--
2.34.1
--u6+Li7FU79Mt/lVd
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v3-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v4 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..47ce61e5093 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -36,7 +36,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index b31f20d41bc..400a8c5d734 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -342,6 +342,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..0fdbaf79780 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -801,6 +809,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..b97184c6539 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,7 +616,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--jg9kqX3osLGO+8A7
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v4-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v5 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 7727fed3bda..42bfb9cd38f 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -37,7 +37,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -48,6 +48,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -261,6 +266,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -285,6 +318,10 @@ pgstat_flush_backend(bool nowait, uint32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -300,6 +337,9 @@ pgstat_flush_backend(bool nowait, uint32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index 933dcb5cae5..218ff5e653c 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -352,6 +352,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 8e3549c3752..5d73c4dfbcf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -523,6 +523,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -535,6 +537,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -844,6 +852,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index eed4c6b359c..8077c65e938 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -707,7 +707,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, uint32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--uN5gGi2rtCNSVbpC
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v5-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v1] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 2 +
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 160 insertions(+), 1 deletion(-)
26.9% doc/src/sgml/
29.1% src/backend/utils/activity/
12.3% src/backend/utils/adt/
9.7% src/include/catalog/
4.0% src/include/utils/
9.2% src/test/regress/expected/
7.3% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index fa78031ccbb..ef89683164f 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..7cec0c83071 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,13 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static int pgStatBackendXactCommit = 0;
+static int pgStatBackendXactRollback = 0;
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +266,33 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += pgStatBackendXactCommit;
+ shbackendent->stats.xact_rollback += pgStatBackendXactRollback;
+
+ pgStatBackendXactCommit = pgStatBackendXactRollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +317,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +336,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +441,22 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ pgStatBackendXactCommit++;
+ else
+ pgStatBackendXactRollback++;
+
+ backend_has_xactstats = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..4c9f6b0dcec 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--ZnbSmJtA6EgjuGCe--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v5 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 7727fed3bda..42bfb9cd38f 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -37,7 +37,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -48,6 +48,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -261,6 +266,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -285,6 +318,10 @@ pgstat_flush_backend(bool nowait, uint32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -300,6 +337,9 @@ pgstat_flush_backend(bool nowait, uint32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index 933dcb5cae5..218ff5e653c 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -352,6 +352,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 8e3549c3752..5d73c4dfbcf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -523,6 +523,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -535,6 +537,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -844,6 +852,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index eed4c6b359c..8077c65e938 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -707,7 +707,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, uint32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--uN5gGi2rtCNSVbpC
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v5-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v1] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 2 +
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 160 insertions(+), 1 deletion(-)
26.9% doc/src/sgml/
29.1% src/backend/utils/activity/
12.3% src/backend/utils/adt/
9.7% src/include/catalog/
4.0% src/include/utils/
9.2% src/test/regress/expected/
7.3% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index fa78031ccbb..ef89683164f 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..7cec0c83071 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,13 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static int pgStatBackendXactCommit = 0;
+static int pgStatBackendXactRollback = 0;
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +266,33 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += pgStatBackendXactCommit;
+ shbackendent->stats.xact_rollback += pgStatBackendXactRollback;
+
+ pgStatBackendXactCommit = pgStatBackendXactRollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +317,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +336,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +441,22 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ pgStatBackendXactCommit++;
+ else
+ pgStatBackendXactRollback++;
+
+ backend_has_xactstats = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..4c9f6b0dcec 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--ZnbSmJtA6EgjuGCe--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v2] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 166 insertions(+), 1 deletion(-)
26.1% doc/src/sgml/
29.1% src/backend/utils/activity/
11.9% src/backend/utils/adt/
9.4% src/include/catalog/
3.9% src/include/utils/
3.3% src/include/
8.9% src/test/regress/expected/
7.1% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 3f4a27a736e..77b41e02b82 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..12ae9a8d321 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,23 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..d3491faaff2 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Xact statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--SywurcztURg2uk+W--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v3 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. A new function is called in AtEOXact_PgStat() to increment those
two new counters.
---
src/backend/utils/activity/pgstat_backend.c | 57 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
4 files changed, 69 insertions(+), 1 deletion(-)
78.9% src/backend/utils/activity/
11.2% src/include/utils/
9.8% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..bf164854c4b 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,20 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /* Count transaction commit or abort */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..9efc0e10ebf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
--
2.34.1
--u6+Li7FU79Mt/lVd
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v3-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v1] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 2 +
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 160 insertions(+), 1 deletion(-)
26.9% doc/src/sgml/
29.1% src/backend/utils/activity/
12.3% src/backend/utils/adt/
9.7% src/include/catalog/
4.0% src/include/utils/
9.2% src/test/regress/expected/
7.3% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index fa78031ccbb..ef89683164f 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..7cec0c83071 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,13 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static int pgStatBackendXactCommit = 0;
+static int pgStatBackendXactRollback = 0;
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +266,33 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += pgStatBackendXactCommit;
+ shbackendent->stats.xact_rollback += pgStatBackendXactRollback;
+
+ pgStatBackendXactCommit = pgStatBackendXactRollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +317,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +336,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +441,22 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ pgStatBackendXactCommit++;
+ else
+ pgStatBackendXactRollback++;
+
+ backend_has_xactstats = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..4c9f6b0dcec 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--ZnbSmJtA6EgjuGCe--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v2] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 166 insertions(+), 1 deletion(-)
26.1% doc/src/sgml/
29.1% src/backend/utils/activity/
11.9% src/backend/utils/adt/
9.4% src/include/catalog/
3.9% src/include/utils/
3.3% src/include/
8.9% src/test/regress/expected/
7.1% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 3f4a27a736e..77b41e02b82 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..12ae9a8d321 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,23 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..d3491faaff2 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Xact statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--SywurcztURg2uk+W--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v3 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. A new function is called in AtEOXact_PgStat() to increment those
two new counters.
---
src/backend/utils/activity/pgstat_backend.c | 57 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
4 files changed, 69 insertions(+), 1 deletion(-)
78.9% src/backend/utils/activity/
11.2% src/include/utils/
9.8% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..bf164854c4b 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,20 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /* Count transaction commit or abort */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..9efc0e10ebf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
--
2.34.1
--u6+Li7FU79Mt/lVd
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v3-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v4 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..47ce61e5093 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -36,7 +36,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index b31f20d41bc..400a8c5d734 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -342,6 +342,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..0fdbaf79780 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -801,6 +809,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..b97184c6539 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,7 +616,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--jg9kqX3osLGO+8A7
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v4-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v5 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 7727fed3bda..42bfb9cd38f 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -37,7 +37,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -48,6 +48,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -261,6 +266,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -285,6 +318,10 @@ pgstat_flush_backend(bool nowait, uint32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -300,6 +337,9 @@ pgstat_flush_backend(bool nowait, uint32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index 933dcb5cae5..218ff5e653c 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -352,6 +352,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 8e3549c3752..5d73c4dfbcf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -523,6 +523,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -535,6 +537,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -844,6 +852,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index eed4c6b359c..8077c65e938 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -707,7 +707,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, uint32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--uN5gGi2rtCNSVbpC
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v5-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v1] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 2 +
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 160 insertions(+), 1 deletion(-)
26.9% doc/src/sgml/
29.1% src/backend/utils/activity/
12.3% src/backend/utils/adt/
9.7% src/include/catalog/
4.0% src/include/utils/
9.2% src/test/regress/expected/
7.3% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index fa78031ccbb..ef89683164f 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..7cec0c83071 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,13 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static int pgStatBackendXactCommit = 0;
+static int pgStatBackendXactRollback = 0;
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +266,33 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += pgStatBackendXactCommit;
+ shbackendent->stats.xact_rollback += pgStatBackendXactRollback;
+
+ pgStatBackendXactCommit = pgStatBackendXactRollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +317,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +336,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +441,22 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ pgStatBackendXactCommit++;
+ else
+ pgStatBackendXactRollback++;
+
+ backend_has_xactstats = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..4c9f6b0dcec 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--ZnbSmJtA6EgjuGCe--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v2] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 166 insertions(+), 1 deletion(-)
26.1% doc/src/sgml/
29.1% src/backend/utils/activity/
11.9% src/backend/utils/adt/
9.4% src/include/catalog/
3.9% src/include/utils/
3.3% src/include/
8.9% src/test/regress/expected/
7.1% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 3f4a27a736e..77b41e02b82 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..12ae9a8d321 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,23 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..d3491faaff2 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Xact statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--SywurcztURg2uk+W--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v3 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. A new function is called in AtEOXact_PgStat() to increment those
two new counters.
---
src/backend/utils/activity/pgstat_backend.c | 57 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
4 files changed, 69 insertions(+), 1 deletion(-)
78.9% src/backend/utils/activity/
11.2% src/include/utils/
9.8% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..bf164854c4b 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,20 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /* Count transaction commit or abort */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..9efc0e10ebf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
--
2.34.1
--u6+Li7FU79Mt/lVd
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v3-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v4 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..47ce61e5093 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -36,7 +36,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index b31f20d41bc..400a8c5d734 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -342,6 +342,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..0fdbaf79780 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -801,6 +809,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..b97184c6539 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,7 +616,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--jg9kqX3osLGO+8A7
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v4-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v5 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 7727fed3bda..42bfb9cd38f 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -37,7 +37,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -48,6 +48,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -261,6 +266,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -285,6 +318,10 @@ pgstat_flush_backend(bool nowait, uint32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -300,6 +337,9 @@ pgstat_flush_backend(bool nowait, uint32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index 933dcb5cae5..218ff5e653c 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -352,6 +352,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 8e3549c3752..5d73c4dfbcf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -523,6 +523,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -535,6 +537,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -844,6 +852,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index eed4c6b359c..8077c65e938 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -707,7 +707,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, uint32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--uN5gGi2rtCNSVbpC
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v5-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v1] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 2 +
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 160 insertions(+), 1 deletion(-)
26.9% doc/src/sgml/
29.1% src/backend/utils/activity/
12.3% src/backend/utils/adt/
9.7% src/include/catalog/
4.0% src/include/utils/
9.2% src/test/regress/expected/
7.3% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index fa78031ccbb..ef89683164f 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..7cec0c83071 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,13 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static int pgStatBackendXactCommit = 0;
+static int pgStatBackendXactRollback = 0;
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +266,33 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += pgStatBackendXactCommit;
+ shbackendent->stats.xact_rollback += pgStatBackendXactRollback;
+
+ pgStatBackendXactCommit = pgStatBackendXactRollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +317,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +336,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +441,22 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ pgStatBackendXactCommit++;
+ else
+ pgStatBackendXactRollback++;
+
+ backend_has_xactstats = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..4c9f6b0dcec 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--ZnbSmJtA6EgjuGCe--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v2] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 166 insertions(+), 1 deletion(-)
26.1% doc/src/sgml/
29.1% src/backend/utils/activity/
11.9% src/backend/utils/adt/
9.4% src/include/catalog/
3.9% src/include/utils/
3.3% src/include/
8.9% src/test/regress/expected/
7.1% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 3f4a27a736e..77b41e02b82 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..12ae9a8d321 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,23 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..d3491faaff2 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Xact statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--SywurcztURg2uk+W--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v3 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. A new function is called in AtEOXact_PgStat() to increment those
two new counters.
---
src/backend/utils/activity/pgstat_backend.c | 57 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
4 files changed, 69 insertions(+), 1 deletion(-)
78.9% src/backend/utils/activity/
11.2% src/include/utils/
9.8% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..bf164854c4b 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,20 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /* Count transaction commit or abort */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..9efc0e10ebf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
--
2.34.1
--u6+Li7FU79Mt/lVd
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v3-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v4 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..47ce61e5093 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -36,7 +36,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index b31f20d41bc..400a8c5d734 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -342,6 +342,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..0fdbaf79780 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -801,6 +809,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..b97184c6539 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,7 +616,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--jg9kqX3osLGO+8A7
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v4-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v5 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 7727fed3bda..42bfb9cd38f 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -37,7 +37,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -48,6 +48,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -261,6 +266,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -285,6 +318,10 @@ pgstat_flush_backend(bool nowait, uint32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -300,6 +337,9 @@ pgstat_flush_backend(bool nowait, uint32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index 933dcb5cae5..218ff5e653c 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -352,6 +352,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 8e3549c3752..5d73c4dfbcf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -523,6 +523,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -535,6 +537,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -844,6 +852,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index eed4c6b359c..8077c65e938 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -707,7 +707,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, uint32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--uN5gGi2rtCNSVbpC
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v5-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v4 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..47ce61e5093 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -36,7 +36,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index b31f20d41bc..400a8c5d734 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -342,6 +342,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..0fdbaf79780 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -801,6 +809,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..b97184c6539 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,7 +616,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--jg9kqX3osLGO+8A7
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v4-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v1] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 2 +
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 160 insertions(+), 1 deletion(-)
26.9% doc/src/sgml/
29.1% src/backend/utils/activity/
12.3% src/backend/utils/adt/
9.7% src/include/catalog/
4.0% src/include/utils/
9.2% src/test/regress/expected/
7.3% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index fa78031ccbb..ef89683164f 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..7cec0c83071 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,13 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static int pgStatBackendXactCommit = 0;
+static int pgStatBackendXactRollback = 0;
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +266,33 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += pgStatBackendXactCommit;
+ shbackendent->stats.xact_rollback += pgStatBackendXactRollback;
+
+ pgStatBackendXactCommit = pgStatBackendXactRollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +317,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +336,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +441,22 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ pgStatBackendXactCommit++;
+ else
+ pgStatBackendXactRollback++;
+
+ backend_has_xactstats = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..4c9f6b0dcec 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--ZnbSmJtA6EgjuGCe--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v2] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 166 insertions(+), 1 deletion(-)
26.1% doc/src/sgml/
29.1% src/backend/utils/activity/
11.9% src/backend/utils/adt/
9.4% src/include/catalog/
3.9% src/include/utils/
3.3% src/include/
8.9% src/test/regress/expected/
7.1% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 3f4a27a736e..77b41e02b82 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..12ae9a8d321 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,23 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..d3491faaff2 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Xact statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--SywurcztURg2uk+W--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v3 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. A new function is called in AtEOXact_PgStat() to increment those
two new counters.
---
src/backend/utils/activity/pgstat_backend.c | 57 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
4 files changed, 69 insertions(+), 1 deletion(-)
78.9% src/backend/utils/activity/
11.2% src/include/utils/
9.8% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..bf164854c4b 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,20 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /* Count transaction commit or abort */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..9efc0e10ebf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
--
2.34.1
--u6+Li7FU79Mt/lVd
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v3-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v4 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..47ce61e5093 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -36,7 +36,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index b31f20d41bc..400a8c5d734 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -342,6 +342,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..0fdbaf79780 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -801,6 +809,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..b97184c6539 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,7 +616,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--jg9kqX3osLGO+8A7
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v4-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v5 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 7727fed3bda..42bfb9cd38f 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -37,7 +37,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -48,6 +48,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -261,6 +266,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -285,6 +318,10 @@ pgstat_flush_backend(bool nowait, uint32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -300,6 +337,9 @@ pgstat_flush_backend(bool nowait, uint32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index 933dcb5cae5..218ff5e653c 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -352,6 +352,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 8e3549c3752..5d73c4dfbcf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -523,6 +523,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -535,6 +537,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -844,6 +852,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index eed4c6b359c..8077c65e938 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -707,7 +707,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, uint32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--uN5gGi2rtCNSVbpC
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v5-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v1] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 2 +
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 160 insertions(+), 1 deletion(-)
26.9% doc/src/sgml/
29.1% src/backend/utils/activity/
12.3% src/backend/utils/adt/
9.7% src/include/catalog/
4.0% src/include/utils/
9.2% src/test/regress/expected/
7.3% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index fa78031ccbb..ef89683164f 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..7cec0c83071 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,13 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static int pgStatBackendXactCommit = 0;
+static int pgStatBackendXactRollback = 0;
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +266,33 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += pgStatBackendXactCommit;
+ shbackendent->stats.xact_rollback += pgStatBackendXactRollback;
+
+ pgStatBackendXactCommit = pgStatBackendXactRollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +317,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +336,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +441,22 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ pgStatBackendXactCommit++;
+ else
+ pgStatBackendXactRollback++;
+
+ backend_has_xactstats = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..4c9f6b0dcec 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--ZnbSmJtA6EgjuGCe--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v2] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 166 insertions(+), 1 deletion(-)
26.1% doc/src/sgml/
29.1% src/backend/utils/activity/
11.9% src/backend/utils/adt/
9.4% src/include/catalog/
3.9% src/include/utils/
3.3% src/include/
8.9% src/test/regress/expected/
7.1% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 3f4a27a736e..77b41e02b82 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..12ae9a8d321 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,23 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..d3491faaff2 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Xact statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--SywurcztURg2uk+W--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v3 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. A new function is called in AtEOXact_PgStat() to increment those
two new counters.
---
src/backend/utils/activity/pgstat_backend.c | 57 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
4 files changed, 69 insertions(+), 1 deletion(-)
78.9% src/backend/utils/activity/
11.2% src/include/utils/
9.8% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..bf164854c4b 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,20 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /* Count transaction commit or abort */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..9efc0e10ebf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
--
2.34.1
--u6+Li7FU79Mt/lVd
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v3-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v4 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..47ce61e5093 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -36,7 +36,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index b31f20d41bc..400a8c5d734 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -342,6 +342,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..0fdbaf79780 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -801,6 +809,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..b97184c6539 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,7 +616,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--jg9kqX3osLGO+8A7
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v4-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v5 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 7727fed3bda..42bfb9cd38f 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -37,7 +37,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -48,6 +48,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -261,6 +266,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -285,6 +318,10 @@ pgstat_flush_backend(bool nowait, uint32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -300,6 +337,9 @@ pgstat_flush_backend(bool nowait, uint32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index 933dcb5cae5..218ff5e653c 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -352,6 +352,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 8e3549c3752..5d73c4dfbcf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -523,6 +523,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -535,6 +537,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -844,6 +852,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index eed4c6b359c..8077c65e938 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -707,7 +707,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, uint32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--uN5gGi2rtCNSVbpC
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v5-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v2] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 166 insertions(+), 1 deletion(-)
26.1% doc/src/sgml/
29.1% src/backend/utils/activity/
11.9% src/backend/utils/adt/
9.4% src/include/catalog/
3.9% src/include/utils/
3.3% src/include/
8.9% src/test/regress/expected/
7.1% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 3f4a27a736e..77b41e02b82 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..12ae9a8d321 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,23 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..d3491faaff2 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Xact statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--SywurcztURg2uk+W--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v3 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. A new function is called in AtEOXact_PgStat() to increment those
two new counters.
---
src/backend/utils/activity/pgstat_backend.c | 57 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
4 files changed, 69 insertions(+), 1 deletion(-)
78.9% src/backend/utils/activity/
11.2% src/include/utils/
9.8% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..bf164854c4b 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,20 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /* Count transaction commit or abort */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..9efc0e10ebf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
--
2.34.1
--u6+Li7FU79Mt/lVd
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v3-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v1] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 2 +
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 160 insertions(+), 1 deletion(-)
26.9% doc/src/sgml/
29.1% src/backend/utils/activity/
12.3% src/backend/utils/adt/
9.7% src/include/catalog/
4.0% src/include/utils/
9.2% src/test/regress/expected/
7.3% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index fa78031ccbb..ef89683164f 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..7cec0c83071 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,13 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static int pgStatBackendXactCommit = 0;
+static int pgStatBackendXactRollback = 0;
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +266,33 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += pgStatBackendXactCommit;
+ shbackendent->stats.xact_rollback += pgStatBackendXactRollback;
+
+ pgStatBackendXactCommit = pgStatBackendXactRollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +317,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +336,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +441,22 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ pgStatBackendXactCommit++;
+ else
+ pgStatBackendXactRollback++;
+
+ backend_has_xactstats = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..4c9f6b0dcec 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--ZnbSmJtA6EgjuGCe--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v2] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 166 insertions(+), 1 deletion(-)
26.1% doc/src/sgml/
29.1% src/backend/utils/activity/
11.9% src/backend/utils/adt/
9.4% src/include/catalog/
3.9% src/include/utils/
3.3% src/include/
8.9% src/test/regress/expected/
7.1% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 3f4a27a736e..77b41e02b82 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..12ae9a8d321 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,23 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..d3491faaff2 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Xact statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--SywurcztURg2uk+W--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v3 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. A new function is called in AtEOXact_PgStat() to increment those
two new counters.
---
src/backend/utils/activity/pgstat_backend.c | 57 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
4 files changed, 69 insertions(+), 1 deletion(-)
78.9% src/backend/utils/activity/
11.2% src/include/utils/
9.8% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..bf164854c4b 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,20 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /* Count transaction commit or abort */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..9efc0e10ebf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
--
2.34.1
--u6+Li7FU79Mt/lVd
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v3-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v4 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..47ce61e5093 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -36,7 +36,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index b31f20d41bc..400a8c5d734 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -342,6 +342,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..0fdbaf79780 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -801,6 +809,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..b97184c6539 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,7 +616,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--jg9kqX3osLGO+8A7
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v4-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v5 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 7727fed3bda..42bfb9cd38f 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -37,7 +37,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -48,6 +48,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -261,6 +266,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -285,6 +318,10 @@ pgstat_flush_backend(bool nowait, uint32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -300,6 +337,9 @@ pgstat_flush_backend(bool nowait, uint32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index 933dcb5cae5..218ff5e653c 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -352,6 +352,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 8e3549c3752..5d73c4dfbcf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -523,6 +523,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -535,6 +537,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -844,6 +852,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index eed4c6b359c..8077c65e938 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -707,7 +707,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, uint32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--uN5gGi2rtCNSVbpC
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v5-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v1] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 2 +
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 160 insertions(+), 1 deletion(-)
26.9% doc/src/sgml/
29.1% src/backend/utils/activity/
12.3% src/backend/utils/adt/
9.7% src/include/catalog/
4.0% src/include/utils/
9.2% src/test/regress/expected/
7.3% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index fa78031ccbb..ef89683164f 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..7cec0c83071 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,13 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static int pgStatBackendXactCommit = 0;
+static int pgStatBackendXactRollback = 0;
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +266,33 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += pgStatBackendXactCommit;
+ shbackendent->stats.xact_rollback += pgStatBackendXactRollback;
+
+ pgStatBackendXactCommit = pgStatBackendXactRollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +317,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +336,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +441,22 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ pgStatBackendXactCommit++;
+ else
+ pgStatBackendXactRollback++;
+
+ backend_has_xactstats = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..4c9f6b0dcec 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--ZnbSmJtA6EgjuGCe--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v2] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 166 insertions(+), 1 deletion(-)
26.1% doc/src/sgml/
29.1% src/backend/utils/activity/
11.9% src/backend/utils/adt/
9.4% src/include/catalog/
3.9% src/include/utils/
3.3% src/include/
8.9% src/test/regress/expected/
7.1% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 3f4a27a736e..77b41e02b82 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..12ae9a8d321 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,23 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..d3491faaff2 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Xact statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--SywurcztURg2uk+W--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v3 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. A new function is called in AtEOXact_PgStat() to increment those
two new counters.
---
src/backend/utils/activity/pgstat_backend.c | 57 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
4 files changed, 69 insertions(+), 1 deletion(-)
78.9% src/backend/utils/activity/
11.2% src/include/utils/
9.8% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..bf164854c4b 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,20 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /* Count transaction commit or abort */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..9efc0e10ebf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
--
2.34.1
--u6+Li7FU79Mt/lVd
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v3-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v4 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..47ce61e5093 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -36,7 +36,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index b31f20d41bc..400a8c5d734 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -342,6 +342,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..0fdbaf79780 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -801,6 +809,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..b97184c6539 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,7 +616,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--jg9kqX3osLGO+8A7
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v4-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v5 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 7727fed3bda..42bfb9cd38f 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -37,7 +37,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -48,6 +48,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -261,6 +266,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -285,6 +318,10 @@ pgstat_flush_backend(bool nowait, uint32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -300,6 +337,9 @@ pgstat_flush_backend(bool nowait, uint32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index 933dcb5cae5..218ff5e653c 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -352,6 +352,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 8e3549c3752..5d73c4dfbcf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -523,6 +523,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -535,6 +537,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -844,6 +852,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index eed4c6b359c..8077c65e938 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -707,7 +707,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, uint32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--uN5gGi2rtCNSVbpC
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v5-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v1] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 2 +
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 160 insertions(+), 1 deletion(-)
26.9% doc/src/sgml/
29.1% src/backend/utils/activity/
12.3% src/backend/utils/adt/
9.7% src/include/catalog/
4.0% src/include/utils/
9.2% src/test/regress/expected/
7.3% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index fa78031ccbb..ef89683164f 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..7cec0c83071 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,13 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static int pgStatBackendXactCommit = 0;
+static int pgStatBackendXactRollback = 0;
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +266,33 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += pgStatBackendXactCommit;
+ shbackendent->stats.xact_rollback += pgStatBackendXactRollback;
+
+ pgStatBackendXactCommit = pgStatBackendXactRollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +317,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +336,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +441,22 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ pgStatBackendXactCommit++;
+ else
+ pgStatBackendXactRollback++;
+
+ backend_has_xactstats = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..4c9f6b0dcec 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--ZnbSmJtA6EgjuGCe--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v2] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 166 insertions(+), 1 deletion(-)
26.1% doc/src/sgml/
29.1% src/backend/utils/activity/
11.9% src/backend/utils/adt/
9.4% src/include/catalog/
3.9% src/include/utils/
3.3% src/include/
8.9% src/test/regress/expected/
7.1% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 3f4a27a736e..77b41e02b82 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..12ae9a8d321 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,23 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..d3491faaff2 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Xact statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--SywurcztURg2uk+W--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v3 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. A new function is called in AtEOXact_PgStat() to increment those
two new counters.
---
src/backend/utils/activity/pgstat_backend.c | 57 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
4 files changed, 69 insertions(+), 1 deletion(-)
78.9% src/backend/utils/activity/
11.2% src/include/utils/
9.8% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..bf164854c4b 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,20 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /* Count transaction commit or abort */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..9efc0e10ebf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
--
2.34.1
--u6+Li7FU79Mt/lVd
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v3-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v4 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..47ce61e5093 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -36,7 +36,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index b31f20d41bc..400a8c5d734 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -342,6 +342,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..0fdbaf79780 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -801,6 +809,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..b97184c6539 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,7 +616,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--jg9kqX3osLGO+8A7
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v4-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v5 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 7727fed3bda..42bfb9cd38f 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -37,7 +37,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -48,6 +48,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -261,6 +266,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -285,6 +318,10 @@ pgstat_flush_backend(bool nowait, uint32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -300,6 +337,9 @@ pgstat_flush_backend(bool nowait, uint32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index 933dcb5cae5..218ff5e653c 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -352,6 +352,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 8e3549c3752..5d73c4dfbcf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -523,6 +523,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -535,6 +537,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -844,6 +852,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index eed4c6b359c..8077c65e938 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -707,7 +707,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, uint32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--uN5gGi2rtCNSVbpC
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v5-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v1] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 2 +
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 160 insertions(+), 1 deletion(-)
26.9% doc/src/sgml/
29.1% src/backend/utils/activity/
12.3% src/backend/utils/adt/
9.7% src/include/catalog/
4.0% src/include/utils/
9.2% src/test/regress/expected/
7.3% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index fa78031ccbb..ef89683164f 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..7cec0c83071 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,13 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static int pgStatBackendXactCommit = 0;
+static int pgStatBackendXactRollback = 0;
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +266,33 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += pgStatBackendXactCommit;
+ shbackendent->stats.xact_rollback += pgStatBackendXactRollback;
+
+ pgStatBackendXactCommit = pgStatBackendXactRollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +317,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +336,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +441,22 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ pgStatBackendXactCommit++;
+ else
+ pgStatBackendXactRollback++;
+
+ backend_has_xactstats = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..4c9f6b0dcec 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--ZnbSmJtA6EgjuGCe--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v2] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 166 insertions(+), 1 deletion(-)
26.1% doc/src/sgml/
29.1% src/backend/utils/activity/
11.9% src/backend/utils/adt/
9.4% src/include/catalog/
3.9% src/include/utils/
3.3% src/include/
8.9% src/test/regress/expected/
7.1% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 3f4a27a736e..77b41e02b82 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..12ae9a8d321 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,23 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..d3491faaff2 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Xact statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--SywurcztURg2uk+W--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v3 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. A new function is called in AtEOXact_PgStat() to increment those
two new counters.
---
src/backend/utils/activity/pgstat_backend.c | 57 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
4 files changed, 69 insertions(+), 1 deletion(-)
78.9% src/backend/utils/activity/
11.2% src/include/utils/
9.8% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..bf164854c4b 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,20 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /* Count transaction commit or abort */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..9efc0e10ebf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
--
2.34.1
--u6+Li7FU79Mt/lVd
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v3-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v4 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..47ce61e5093 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -36,7 +36,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index b31f20d41bc..400a8c5d734 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -342,6 +342,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..0fdbaf79780 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -801,6 +809,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..b97184c6539 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,7 +616,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--jg9kqX3osLGO+8A7
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v4-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v5 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 7727fed3bda..42bfb9cd38f 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -37,7 +37,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -48,6 +48,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -261,6 +266,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -285,6 +318,10 @@ pgstat_flush_backend(bool nowait, uint32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -300,6 +337,9 @@ pgstat_flush_backend(bool nowait, uint32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index 933dcb5cae5..218ff5e653c 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -352,6 +352,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 8e3549c3752..5d73c4dfbcf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -523,6 +523,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -535,6 +537,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -844,6 +852,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index eed4c6b359c..8077c65e938 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -707,7 +707,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, uint32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--uN5gGi2rtCNSVbpC
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v5-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v1] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 2 +
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 160 insertions(+), 1 deletion(-)
26.9% doc/src/sgml/
29.1% src/backend/utils/activity/
12.3% src/backend/utils/adt/
9.7% src/include/catalog/
4.0% src/include/utils/
9.2% src/test/regress/expected/
7.3% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index fa78031ccbb..ef89683164f 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..7cec0c83071 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,13 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static int pgStatBackendXactCommit = 0;
+static int pgStatBackendXactRollback = 0;
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +266,33 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += pgStatBackendXactCommit;
+ shbackendent->stats.xact_rollback += pgStatBackendXactRollback;
+
+ pgStatBackendXactCommit = pgStatBackendXactRollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +317,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +336,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +441,22 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ pgStatBackendXactCommit++;
+ else
+ pgStatBackendXactRollback++;
+
+ backend_has_xactstats = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..4c9f6b0dcec 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--ZnbSmJtA6EgjuGCe--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v2] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 166 insertions(+), 1 deletion(-)
26.1% doc/src/sgml/
29.1% src/backend/utils/activity/
11.9% src/backend/utils/adt/
9.4% src/include/catalog/
3.9% src/include/utils/
3.3% src/include/
8.9% src/test/regress/expected/
7.1% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 3f4a27a736e..77b41e02b82 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..12ae9a8d321 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,23 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..d3491faaff2 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Xact statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--SywurcztURg2uk+W--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v3 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. A new function is called in AtEOXact_PgStat() to increment those
two new counters.
---
src/backend/utils/activity/pgstat_backend.c | 57 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
4 files changed, 69 insertions(+), 1 deletion(-)
78.9% src/backend/utils/activity/
11.2% src/include/utils/
9.8% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..bf164854c4b 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,20 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /* Count transaction commit or abort */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..9efc0e10ebf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
--
2.34.1
--u6+Li7FU79Mt/lVd
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v3-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v4 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..47ce61e5093 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -36,7 +36,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index b31f20d41bc..400a8c5d734 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -342,6 +342,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..0fdbaf79780 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -801,6 +809,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..b97184c6539 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,7 +616,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--jg9kqX3osLGO+8A7
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v4-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v5 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 7727fed3bda..42bfb9cd38f 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -37,7 +37,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -48,6 +48,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -261,6 +266,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -285,6 +318,10 @@ pgstat_flush_backend(bool nowait, uint32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -300,6 +337,9 @@ pgstat_flush_backend(bool nowait, uint32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index 933dcb5cae5..218ff5e653c 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -352,6 +352,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 8e3549c3752..5d73c4dfbcf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -523,6 +523,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -535,6 +537,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -844,6 +852,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index eed4c6b359c..8077c65e938 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -707,7 +707,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, uint32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--uN5gGi2rtCNSVbpC
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v5-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v1] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 2 +
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 160 insertions(+), 1 deletion(-)
26.9% doc/src/sgml/
29.1% src/backend/utils/activity/
12.3% src/backend/utils/adt/
9.7% src/include/catalog/
4.0% src/include/utils/
9.2% src/test/regress/expected/
7.3% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index fa78031ccbb..ef89683164f 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..7cec0c83071 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,13 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static int pgStatBackendXactCommit = 0;
+static int pgStatBackendXactRollback = 0;
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +266,33 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += pgStatBackendXactCommit;
+ shbackendent->stats.xact_rollback += pgStatBackendXactRollback;
+
+ pgStatBackendXactCommit = pgStatBackendXactRollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +317,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +336,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +441,22 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ pgStatBackendXactCommit++;
+ else
+ pgStatBackendXactRollback++;
+
+ backend_has_xactstats = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..4c9f6b0dcec 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--ZnbSmJtA6EgjuGCe--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v2] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 166 insertions(+), 1 deletion(-)
26.1% doc/src/sgml/
29.1% src/backend/utils/activity/
11.9% src/backend/utils/adt/
9.4% src/include/catalog/
3.9% src/include/utils/
3.3% src/include/
8.9% src/test/regress/expected/
7.1% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 3f4a27a736e..77b41e02b82 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..12ae9a8d321 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,23 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..d3491faaff2 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Xact statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--SywurcztURg2uk+W--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v3 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. A new function is called in AtEOXact_PgStat() to increment those
two new counters.
---
src/backend/utils/activity/pgstat_backend.c | 57 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
4 files changed, 69 insertions(+), 1 deletion(-)
78.9% src/backend/utils/activity/
11.2% src/include/utils/
9.8% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..bf164854c4b 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,20 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /* Count transaction commit or abort */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..9efc0e10ebf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
--
2.34.1
--u6+Li7FU79Mt/lVd
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v3-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v4 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..47ce61e5093 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -36,7 +36,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index b31f20d41bc..400a8c5d734 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -342,6 +342,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..0fdbaf79780 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -801,6 +809,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..b97184c6539 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,7 +616,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--jg9kqX3osLGO+8A7
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v4-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v5 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 7727fed3bda..42bfb9cd38f 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -37,7 +37,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -48,6 +48,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -261,6 +266,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -285,6 +318,10 @@ pgstat_flush_backend(bool nowait, uint32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -300,6 +337,9 @@ pgstat_flush_backend(bool nowait, uint32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index 933dcb5cae5..218ff5e653c 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -352,6 +352,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 8e3549c3752..5d73c4dfbcf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -523,6 +523,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -535,6 +537,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -844,6 +852,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index eed4c6b359c..8077c65e938 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -707,7 +707,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, uint32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--uN5gGi2rtCNSVbpC
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v5-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v1] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 2 +
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 160 insertions(+), 1 deletion(-)
26.9% doc/src/sgml/
29.1% src/backend/utils/activity/
12.3% src/backend/utils/adt/
9.7% src/include/catalog/
4.0% src/include/utils/
9.2% src/test/regress/expected/
7.3% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index fa78031ccbb..ef89683164f 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..7cec0c83071 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,13 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static int pgStatBackendXactCommit = 0;
+static int pgStatBackendXactRollback = 0;
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +266,33 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += pgStatBackendXactCommit;
+ shbackendent->stats.xact_rollback += pgStatBackendXactRollback;
+
+ pgStatBackendXactCommit = pgStatBackendXactRollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +317,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +336,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +441,22 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ pgStatBackendXactCommit++;
+ else
+ pgStatBackendXactRollback++;
+
+ backend_has_xactstats = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..4c9f6b0dcec 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--ZnbSmJtA6EgjuGCe--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v2] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 166 insertions(+), 1 deletion(-)
26.1% doc/src/sgml/
29.1% src/backend/utils/activity/
11.9% src/backend/utils/adt/
9.4% src/include/catalog/
3.9% src/include/utils/
3.3% src/include/
8.9% src/test/regress/expected/
7.1% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 3f4a27a736e..77b41e02b82 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..12ae9a8d321 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,23 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..d3491faaff2 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Xact statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--SywurcztURg2uk+W--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v3 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. A new function is called in AtEOXact_PgStat() to increment those
two new counters.
---
src/backend/utils/activity/pgstat_backend.c | 57 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
4 files changed, 69 insertions(+), 1 deletion(-)
78.9% src/backend/utils/activity/
11.2% src/include/utils/
9.8% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..bf164854c4b 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,20 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /* Count transaction commit or abort */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..9efc0e10ebf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
--
2.34.1
--u6+Li7FU79Mt/lVd
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v3-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v4 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..47ce61e5093 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -36,7 +36,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index b31f20d41bc..400a8c5d734 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -342,6 +342,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..0fdbaf79780 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -801,6 +809,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..b97184c6539 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,7 +616,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--jg9kqX3osLGO+8A7
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v4-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v5 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 7727fed3bda..42bfb9cd38f 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -37,7 +37,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -48,6 +48,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -261,6 +266,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -285,6 +318,10 @@ pgstat_flush_backend(bool nowait, uint32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -300,6 +337,9 @@ pgstat_flush_backend(bool nowait, uint32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index 933dcb5cae5..218ff5e653c 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -352,6 +352,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 8e3549c3752..5d73c4dfbcf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -523,6 +523,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -535,6 +537,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -844,6 +852,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index eed4c6b359c..8077c65e938 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -707,7 +707,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, uint32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--uN5gGi2rtCNSVbpC
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v5-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v1] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 2 +
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 160 insertions(+), 1 deletion(-)
26.9% doc/src/sgml/
29.1% src/backend/utils/activity/
12.3% src/backend/utils/adt/
9.7% src/include/catalog/
4.0% src/include/utils/
9.2% src/test/regress/expected/
7.3% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index fa78031ccbb..ef89683164f 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..7cec0c83071 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,13 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static int pgStatBackendXactCommit = 0;
+static int pgStatBackendXactRollback = 0;
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +266,33 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += pgStatBackendXactCommit;
+ shbackendent->stats.xact_rollback += pgStatBackendXactRollback;
+
+ pgStatBackendXactCommit = pgStatBackendXactRollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +317,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +336,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +441,22 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ pgStatBackendXactCommit++;
+ else
+ pgStatBackendXactRollback++;
+
+ backend_has_xactstats = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..4c9f6b0dcec 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--ZnbSmJtA6EgjuGCe--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v2] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 166 insertions(+), 1 deletion(-)
26.1% doc/src/sgml/
29.1% src/backend/utils/activity/
11.9% src/backend/utils/adt/
9.4% src/include/catalog/
3.9% src/include/utils/
3.3% src/include/
8.9% src/test/regress/expected/
7.1% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 3f4a27a736e..77b41e02b82 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..12ae9a8d321 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,23 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..d3491faaff2 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Xact statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--SywurcztURg2uk+W--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v3 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. A new function is called in AtEOXact_PgStat() to increment those
two new counters.
---
src/backend/utils/activity/pgstat_backend.c | 57 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
4 files changed, 69 insertions(+), 1 deletion(-)
78.9% src/backend/utils/activity/
11.2% src/include/utils/
9.8% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..bf164854c4b 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,20 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /* Count transaction commit or abort */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..9efc0e10ebf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
--
2.34.1
--u6+Li7FU79Mt/lVd
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v3-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v1] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 2 +
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 160 insertions(+), 1 deletion(-)
26.9% doc/src/sgml/
29.1% src/backend/utils/activity/
12.3% src/backend/utils/adt/
9.7% src/include/catalog/
4.0% src/include/utils/
9.2% src/test/regress/expected/
7.3% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index fa78031ccbb..ef89683164f 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..7cec0c83071 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,13 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static int pgStatBackendXactCommit = 0;
+static int pgStatBackendXactRollback = 0;
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +266,33 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += pgStatBackendXactCommit;
+ shbackendent->stats.xact_rollback += pgStatBackendXactRollback;
+
+ pgStatBackendXactCommit = pgStatBackendXactRollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +317,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +336,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +441,22 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ pgStatBackendXactCommit++;
+ else
+ pgStatBackendXactRollback++;
+
+ backend_has_xactstats = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..4c9f6b0dcec 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--ZnbSmJtA6EgjuGCe--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v2] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 166 insertions(+), 1 deletion(-)
26.1% doc/src/sgml/
29.1% src/backend/utils/activity/
11.9% src/backend/utils/adt/
9.4% src/include/catalog/
3.9% src/include/utils/
3.3% src/include/
8.9% src/test/regress/expected/
7.1% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 3f4a27a736e..77b41e02b82 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..12ae9a8d321 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,23 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..d3491faaff2 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Xact statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--SywurcztURg2uk+W--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v3 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. A new function is called in AtEOXact_PgStat() to increment those
two new counters.
---
src/backend/utils/activity/pgstat_backend.c | 57 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
4 files changed, 69 insertions(+), 1 deletion(-)
78.9% src/backend/utils/activity/
11.2% src/include/utils/
9.8% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..bf164854c4b 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,20 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /* Count transaction commit or abort */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..9efc0e10ebf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
--
2.34.1
--u6+Li7FU79Mt/lVd
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v3-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v4 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..47ce61e5093 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -36,7 +36,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index b31f20d41bc..400a8c5d734 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -342,6 +342,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..0fdbaf79780 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -801,6 +809,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..b97184c6539 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,7 +616,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--jg9kqX3osLGO+8A7
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v4-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v5 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 7727fed3bda..42bfb9cd38f 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -37,7 +37,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -48,6 +48,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -261,6 +266,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -285,6 +318,10 @@ pgstat_flush_backend(bool nowait, uint32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -300,6 +337,9 @@ pgstat_flush_backend(bool nowait, uint32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index 933dcb5cae5..218ff5e653c 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -352,6 +352,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 8e3549c3752..5d73c4dfbcf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -523,6 +523,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -535,6 +537,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -844,6 +852,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index eed4c6b359c..8077c65e938 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -707,7 +707,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, uint32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--uN5gGi2rtCNSVbpC
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v5-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v4 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..47ce61e5093 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -36,7 +36,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index b31f20d41bc..400a8c5d734 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -342,6 +342,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..0fdbaf79780 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -801,6 +809,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..b97184c6539 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,7 +616,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--jg9kqX3osLGO+8A7
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v4-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v5 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 7727fed3bda..42bfb9cd38f 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -37,7 +37,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -48,6 +48,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -261,6 +266,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -285,6 +318,10 @@ pgstat_flush_backend(bool nowait, uint32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -300,6 +337,9 @@ pgstat_flush_backend(bool nowait, uint32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index 933dcb5cae5..218ff5e653c 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -352,6 +352,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 8e3549c3752..5d73c4dfbcf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -523,6 +523,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -535,6 +537,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -844,6 +852,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index eed4c6b359c..8077c65e938 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -707,7 +707,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, uint32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--uN5gGi2rtCNSVbpC
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v5-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v4 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..47ce61e5093 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -36,7 +36,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index b31f20d41bc..400a8c5d734 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -342,6 +342,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..0fdbaf79780 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -801,6 +809,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..b97184c6539 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,7 +616,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--jg9kqX3osLGO+8A7
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v4-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v5 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 7727fed3bda..42bfb9cd38f 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -37,7 +37,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -48,6 +48,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -261,6 +266,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -285,6 +318,10 @@ pgstat_flush_backend(bool nowait, uint32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -300,6 +337,9 @@ pgstat_flush_backend(bool nowait, uint32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index 933dcb5cae5..218ff5e653c 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -352,6 +352,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 8e3549c3752..5d73c4dfbcf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -523,6 +523,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -535,6 +537,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -844,6 +852,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index eed4c6b359c..8077c65e938 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -707,7 +707,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, uint32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--uN5gGi2rtCNSVbpC
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v5-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v1] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 2 +
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 160 insertions(+), 1 deletion(-)
26.9% doc/src/sgml/
29.1% src/backend/utils/activity/
12.3% src/backend/utils/adt/
9.7% src/include/catalog/
4.0% src/include/utils/
9.2% src/test/regress/expected/
7.3% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index fa78031ccbb..ef89683164f 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..7cec0c83071 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,13 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static int pgStatBackendXactCommit = 0;
+static int pgStatBackendXactRollback = 0;
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +266,33 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += pgStatBackendXactCommit;
+ shbackendent->stats.xact_rollback += pgStatBackendXactRollback;
+
+ pgStatBackendXactCommit = pgStatBackendXactRollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +317,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +336,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +441,22 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ pgStatBackendXactCommit++;
+ else
+ pgStatBackendXactRollback++;
+
+ backend_has_xactstats = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..4c9f6b0dcec 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--ZnbSmJtA6EgjuGCe--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v2] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 166 insertions(+), 1 deletion(-)
26.1% doc/src/sgml/
29.1% src/backend/utils/activity/
11.9% src/backend/utils/adt/
9.4% src/include/catalog/
3.9% src/include/utils/
3.3% src/include/
8.9% src/test/regress/expected/
7.1% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 3f4a27a736e..77b41e02b82 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..12ae9a8d321 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,23 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..d3491faaff2 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Xact statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--SywurcztURg2uk+W--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v3 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. A new function is called in AtEOXact_PgStat() to increment those
two new counters.
---
src/backend/utils/activity/pgstat_backend.c | 57 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
4 files changed, 69 insertions(+), 1 deletion(-)
78.9% src/backend/utils/activity/
11.2% src/include/utils/
9.8% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..bf164854c4b 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,20 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /* Count transaction commit or abort */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..9efc0e10ebf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
--
2.34.1
--u6+Li7FU79Mt/lVd
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v3-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v4 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..47ce61e5093 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -36,7 +36,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index b31f20d41bc..400a8c5d734 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -342,6 +342,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..0fdbaf79780 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -801,6 +809,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..b97184c6539 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,7 +616,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--jg9kqX3osLGO+8A7
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v4-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v5 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 7727fed3bda..42bfb9cd38f 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -37,7 +37,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -48,6 +48,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -261,6 +266,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -285,6 +318,10 @@ pgstat_flush_backend(bool nowait, uint32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -300,6 +337,9 @@ pgstat_flush_backend(bool nowait, uint32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index 933dcb5cae5..218ff5e653c 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -352,6 +352,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 8e3549c3752..5d73c4dfbcf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -523,6 +523,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -535,6 +537,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -844,6 +852,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index eed4c6b359c..8077c65e938 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -707,7 +707,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, uint32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--uN5gGi2rtCNSVbpC
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v5-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v1] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 2 +
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 160 insertions(+), 1 deletion(-)
26.9% doc/src/sgml/
29.1% src/backend/utils/activity/
12.3% src/backend/utils/adt/
9.7% src/include/catalog/
4.0% src/include/utils/
9.2% src/test/regress/expected/
7.3% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index fa78031ccbb..ef89683164f 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..7cec0c83071 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,13 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static int pgStatBackendXactCommit = 0;
+static int pgStatBackendXactRollback = 0;
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +266,33 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += pgStatBackendXactCommit;
+ shbackendent->stats.xact_rollback += pgStatBackendXactRollback;
+
+ pgStatBackendXactCommit = pgStatBackendXactRollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +317,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +336,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +441,22 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ pgStatBackendXactCommit++;
+ else
+ pgStatBackendXactRollback++;
+
+ backend_has_xactstats = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..4c9f6b0dcec 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--ZnbSmJtA6EgjuGCe--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v2] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 166 insertions(+), 1 deletion(-)
26.1% doc/src/sgml/
29.1% src/backend/utils/activity/
11.9% src/backend/utils/adt/
9.4% src/include/catalog/
3.9% src/include/utils/
3.3% src/include/
8.9% src/test/regress/expected/
7.1% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 3f4a27a736e..77b41e02b82 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..12ae9a8d321 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,23 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..d3491faaff2 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Xact statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--SywurcztURg2uk+W--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v3 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. A new function is called in AtEOXact_PgStat() to increment those
two new counters.
---
src/backend/utils/activity/pgstat_backend.c | 57 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
4 files changed, 69 insertions(+), 1 deletion(-)
78.9% src/backend/utils/activity/
11.2% src/include/utils/
9.8% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..bf164854c4b 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,20 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /* Count transaction commit or abort */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..9efc0e10ebf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
--
2.34.1
--u6+Li7FU79Mt/lVd
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v3-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v4 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..47ce61e5093 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -36,7 +36,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index b31f20d41bc..400a8c5d734 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -342,6 +342,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..0fdbaf79780 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -801,6 +809,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..b97184c6539 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,7 +616,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--jg9kqX3osLGO+8A7
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v4-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v5 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 7727fed3bda..42bfb9cd38f 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -37,7 +37,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -48,6 +48,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -261,6 +266,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -285,6 +318,10 @@ pgstat_flush_backend(bool nowait, uint32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -300,6 +337,9 @@ pgstat_flush_backend(bool nowait, uint32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index 933dcb5cae5..218ff5e653c 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -352,6 +352,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 8e3549c3752..5d73c4dfbcf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -523,6 +523,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -535,6 +537,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -844,6 +852,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index eed4c6b359c..8077c65e938 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -707,7 +707,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, uint32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--uN5gGi2rtCNSVbpC
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v5-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v1] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 2 +
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 160 insertions(+), 1 deletion(-)
26.9% doc/src/sgml/
29.1% src/backend/utils/activity/
12.3% src/backend/utils/adt/
9.7% src/include/catalog/
4.0% src/include/utils/
9.2% src/test/regress/expected/
7.3% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index fa78031ccbb..ef89683164f 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..7cec0c83071 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,13 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static int pgStatBackendXactCommit = 0;
+static int pgStatBackendXactRollback = 0;
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +266,33 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += pgStatBackendXactCommit;
+ shbackendent->stats.xact_rollback += pgStatBackendXactRollback;
+
+ pgStatBackendXactCommit = pgStatBackendXactRollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +317,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +336,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +441,22 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ pgStatBackendXactCommit++;
+ else
+ pgStatBackendXactRollback++;
+
+ backend_has_xactstats = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..4c9f6b0dcec 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--ZnbSmJtA6EgjuGCe--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v2] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 166 insertions(+), 1 deletion(-)
26.1% doc/src/sgml/
29.1% src/backend/utils/activity/
11.9% src/backend/utils/adt/
9.4% src/include/catalog/
3.9% src/include/utils/
3.3% src/include/
8.9% src/test/regress/expected/
7.1% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 3f4a27a736e..77b41e02b82 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..12ae9a8d321 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,23 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..d3491faaff2 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Xact statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--SywurcztURg2uk+W--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v3 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. A new function is called in AtEOXact_PgStat() to increment those
two new counters.
---
src/backend/utils/activity/pgstat_backend.c | 57 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
4 files changed, 69 insertions(+), 1 deletion(-)
78.9% src/backend/utils/activity/
11.2% src/include/utils/
9.8% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..bf164854c4b 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,20 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /* Count transaction commit or abort */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..9efc0e10ebf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
--
2.34.1
--u6+Li7FU79Mt/lVd
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v3-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v4 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..47ce61e5093 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -36,7 +36,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index b31f20d41bc..400a8c5d734 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -342,6 +342,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..0fdbaf79780 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -801,6 +809,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..b97184c6539 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,7 +616,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--jg9kqX3osLGO+8A7
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v4-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v5 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 7727fed3bda..42bfb9cd38f 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -37,7 +37,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -48,6 +48,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -261,6 +266,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -285,6 +318,10 @@ pgstat_flush_backend(bool nowait, uint32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -300,6 +337,9 @@ pgstat_flush_backend(bool nowait, uint32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index 933dcb5cae5..218ff5e653c 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -352,6 +352,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 8e3549c3752..5d73c4dfbcf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -523,6 +523,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -535,6 +537,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -844,6 +852,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index eed4c6b359c..8077c65e938 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -707,7 +707,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, uint32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--uN5gGi2rtCNSVbpC
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v5-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v4 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..47ce61e5093 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -36,7 +36,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index b31f20d41bc..400a8c5d734 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -342,6 +342,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..0fdbaf79780 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -801,6 +809,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..b97184c6539 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,7 +616,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--jg9kqX3osLGO+8A7
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v4-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v1] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 2 +
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 160 insertions(+), 1 deletion(-)
26.9% doc/src/sgml/
29.1% src/backend/utils/activity/
12.3% src/backend/utils/adt/
9.7% src/include/catalog/
4.0% src/include/utils/
9.2% src/test/regress/expected/
7.3% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index fa78031ccbb..ef89683164f 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..7cec0c83071 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,13 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static int pgStatBackendXactCommit = 0;
+static int pgStatBackendXactRollback = 0;
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +266,33 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += pgStatBackendXactCommit;
+ shbackendent->stats.xact_rollback += pgStatBackendXactRollback;
+
+ pgStatBackendXactCommit = pgStatBackendXactRollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +317,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +336,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +441,22 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ pgStatBackendXactCommit++;
+ else
+ pgStatBackendXactRollback++;
+
+ backend_has_xactstats = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..4c9f6b0dcec 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--ZnbSmJtA6EgjuGCe--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v2] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 166 insertions(+), 1 deletion(-)
26.1% doc/src/sgml/
29.1% src/backend/utils/activity/
11.9% src/backend/utils/adt/
9.4% src/include/catalog/
3.9% src/include/utils/
3.3% src/include/
8.9% src/test/regress/expected/
7.1% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 3f4a27a736e..77b41e02b82 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..12ae9a8d321 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,23 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..d3491faaff2 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Xact statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--SywurcztURg2uk+W--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v3 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. A new function is called in AtEOXact_PgStat() to increment those
two new counters.
---
src/backend/utils/activity/pgstat_backend.c | 57 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
4 files changed, 69 insertions(+), 1 deletion(-)
78.9% src/backend/utils/activity/
11.2% src/include/utils/
9.8% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..bf164854c4b 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,20 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /* Count transaction commit or abort */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..9efc0e10ebf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
--
2.34.1
--u6+Li7FU79Mt/lVd
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v3-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v4 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..47ce61e5093 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -36,7 +36,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index b31f20d41bc..400a8c5d734 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -342,6 +342,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..0fdbaf79780 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -801,6 +809,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..b97184c6539 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,7 +616,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--jg9kqX3osLGO+8A7
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v4-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v5 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 7727fed3bda..42bfb9cd38f 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -37,7 +37,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -48,6 +48,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -261,6 +266,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -285,6 +318,10 @@ pgstat_flush_backend(bool nowait, uint32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -300,6 +337,9 @@ pgstat_flush_backend(bool nowait, uint32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index 933dcb5cae5..218ff5e653c 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -352,6 +352,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 8e3549c3752..5d73c4dfbcf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -523,6 +523,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -535,6 +537,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -844,6 +852,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index eed4c6b359c..8077c65e938 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -707,7 +707,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, uint32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--uN5gGi2rtCNSVbpC
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v5-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v1] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 2 +
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 160 insertions(+), 1 deletion(-)
26.9% doc/src/sgml/
29.1% src/backend/utils/activity/
12.3% src/backend/utils/adt/
9.7% src/include/catalog/
4.0% src/include/utils/
9.2% src/test/regress/expected/
7.3% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index fa78031ccbb..ef89683164f 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..7cec0c83071 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,13 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static int pgStatBackendXactCommit = 0;
+static int pgStatBackendXactRollback = 0;
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +266,33 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += pgStatBackendXactCommit;
+ shbackendent->stats.xact_rollback += pgStatBackendXactRollback;
+
+ pgStatBackendXactCommit = pgStatBackendXactRollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +317,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +336,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +441,22 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ pgStatBackendXactCommit++;
+ else
+ pgStatBackendXactRollback++;
+
+ backend_has_xactstats = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..4c9f6b0dcec 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--ZnbSmJtA6EgjuGCe--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v2] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 166 insertions(+), 1 deletion(-)
26.1% doc/src/sgml/
29.1% src/backend/utils/activity/
11.9% src/backend/utils/adt/
9.4% src/include/catalog/
3.9% src/include/utils/
3.3% src/include/
8.9% src/test/regress/expected/
7.1% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 3f4a27a736e..77b41e02b82 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..12ae9a8d321 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,23 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..d3491faaff2 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Xact statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--SywurcztURg2uk+W--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v3 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. A new function is called in AtEOXact_PgStat() to increment those
two new counters.
---
src/backend/utils/activity/pgstat_backend.c | 57 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
4 files changed, 69 insertions(+), 1 deletion(-)
78.9% src/backend/utils/activity/
11.2% src/include/utils/
9.8% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..bf164854c4b 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,20 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /* Count transaction commit or abort */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..9efc0e10ebf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
--
2.34.1
--u6+Li7FU79Mt/lVd
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v3-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v4 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..47ce61e5093 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -36,7 +36,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index b31f20d41bc..400a8c5d734 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -342,6 +342,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..0fdbaf79780 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -801,6 +809,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..b97184c6539 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,7 +616,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--jg9kqX3osLGO+8A7
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v4-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v5 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 7727fed3bda..42bfb9cd38f 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -37,7 +37,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -48,6 +48,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -261,6 +266,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -285,6 +318,10 @@ pgstat_flush_backend(bool nowait, uint32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -300,6 +337,9 @@ pgstat_flush_backend(bool nowait, uint32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index 933dcb5cae5..218ff5e653c 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -352,6 +352,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 8e3549c3752..5d73c4dfbcf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -523,6 +523,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -535,6 +537,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -844,6 +852,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index eed4c6b359c..8077c65e938 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -707,7 +707,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, uint32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--uN5gGi2rtCNSVbpC
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v5-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v4 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..47ce61e5093 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -36,7 +36,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index b31f20d41bc..400a8c5d734 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -342,6 +342,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..0fdbaf79780 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -801,6 +809,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..b97184c6539 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,7 +616,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--jg9kqX3osLGO+8A7
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v4-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v5 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 7727fed3bda..42bfb9cd38f 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -37,7 +37,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -48,6 +48,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -261,6 +266,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -285,6 +318,10 @@ pgstat_flush_backend(bool nowait, uint32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -300,6 +337,9 @@ pgstat_flush_backend(bool nowait, uint32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index 933dcb5cae5..218ff5e653c 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -352,6 +352,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 8e3549c3752..5d73c4dfbcf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -523,6 +523,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -535,6 +537,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -844,6 +852,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index eed4c6b359c..8077c65e938 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -707,7 +707,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, uint32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--uN5gGi2rtCNSVbpC
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v5-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v1] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 2 +
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 160 insertions(+), 1 deletion(-)
26.9% doc/src/sgml/
29.1% src/backend/utils/activity/
12.3% src/backend/utils/adt/
9.7% src/include/catalog/
4.0% src/include/utils/
9.2% src/test/regress/expected/
7.3% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index fa78031ccbb..ef89683164f 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..7cec0c83071 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,13 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static int pgStatBackendXactCommit = 0;
+static int pgStatBackendXactRollback = 0;
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +266,33 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += pgStatBackendXactCommit;
+ shbackendent->stats.xact_rollback += pgStatBackendXactRollback;
+
+ pgStatBackendXactCommit = pgStatBackendXactRollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +317,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +336,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +441,22 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ pgStatBackendXactCommit++;
+ else
+ pgStatBackendXactRollback++;
+
+ backend_has_xactstats = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..4c9f6b0dcec 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--ZnbSmJtA6EgjuGCe--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v2] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 166 insertions(+), 1 deletion(-)
26.1% doc/src/sgml/
29.1% src/backend/utils/activity/
11.9% src/backend/utils/adt/
9.4% src/include/catalog/
3.9% src/include/utils/
3.3% src/include/
8.9% src/test/regress/expected/
7.1% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 3f4a27a736e..77b41e02b82 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..12ae9a8d321 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,23 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..d3491faaff2 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Xact statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--SywurcztURg2uk+W--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v3 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. A new function is called in AtEOXact_PgStat() to increment those
two new counters.
---
src/backend/utils/activity/pgstat_backend.c | 57 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
4 files changed, 69 insertions(+), 1 deletion(-)
78.9% src/backend/utils/activity/
11.2% src/include/utils/
9.8% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..bf164854c4b 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,20 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /* Count transaction commit or abort */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..9efc0e10ebf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
--
2.34.1
--u6+Li7FU79Mt/lVd
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v3-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v4 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..47ce61e5093 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -36,7 +36,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index b31f20d41bc..400a8c5d734 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -342,6 +342,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..0fdbaf79780 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -801,6 +809,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..b97184c6539 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,7 +616,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--jg9kqX3osLGO+8A7
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v4-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v5 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 7727fed3bda..42bfb9cd38f 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -37,7 +37,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -48,6 +48,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -261,6 +266,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -285,6 +318,10 @@ pgstat_flush_backend(bool nowait, uint32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -300,6 +337,9 @@ pgstat_flush_backend(bool nowait, uint32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index 933dcb5cae5..218ff5e653c 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -352,6 +352,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 8e3549c3752..5d73c4dfbcf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -523,6 +523,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -535,6 +537,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -844,6 +852,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index eed4c6b359c..8077c65e938 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -707,7 +707,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, uint32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--uN5gGi2rtCNSVbpC
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v5-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v1] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 2 +
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 160 insertions(+), 1 deletion(-)
26.9% doc/src/sgml/
29.1% src/backend/utils/activity/
12.3% src/backend/utils/adt/
9.7% src/include/catalog/
4.0% src/include/utils/
9.2% src/test/regress/expected/
7.3% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index fa78031ccbb..ef89683164f 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..7cec0c83071 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,13 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static int pgStatBackendXactCommit = 0;
+static int pgStatBackendXactRollback = 0;
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +266,33 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += pgStatBackendXactCommit;
+ shbackendent->stats.xact_rollback += pgStatBackendXactRollback;
+
+ pgStatBackendXactCommit = pgStatBackendXactRollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +317,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +336,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +441,22 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ pgStatBackendXactCommit++;
+ else
+ pgStatBackendXactRollback++;
+
+ backend_has_xactstats = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..4c9f6b0dcec 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--ZnbSmJtA6EgjuGCe--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v2] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 166 insertions(+), 1 deletion(-)
26.1% doc/src/sgml/
29.1% src/backend/utils/activity/
11.9% src/backend/utils/adt/
9.4% src/include/catalog/
3.9% src/include/utils/
3.3% src/include/
8.9% src/test/regress/expected/
7.1% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 3f4a27a736e..77b41e02b82 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..12ae9a8d321 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,23 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..d3491faaff2 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Xact statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--SywurcztURg2uk+W--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v3 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. A new function is called in AtEOXact_PgStat() to increment those
two new counters.
---
src/backend/utils/activity/pgstat_backend.c | 57 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
4 files changed, 69 insertions(+), 1 deletion(-)
78.9% src/backend/utils/activity/
11.2% src/include/utils/
9.8% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..bf164854c4b 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,20 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /* Count transaction commit or abort */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..9efc0e10ebf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
--
2.34.1
--u6+Li7FU79Mt/lVd
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v3-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v4 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..47ce61e5093 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -36,7 +36,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index b31f20d41bc..400a8c5d734 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -342,6 +342,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..0fdbaf79780 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -801,6 +809,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..b97184c6539 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,7 +616,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--jg9kqX3osLGO+8A7
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v4-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v5 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 7727fed3bda..42bfb9cd38f 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -37,7 +37,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -48,6 +48,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -261,6 +266,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -285,6 +318,10 @@ pgstat_flush_backend(bool nowait, uint32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -300,6 +337,9 @@ pgstat_flush_backend(bool nowait, uint32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index 933dcb5cae5..218ff5e653c 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -352,6 +352,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 8e3549c3752..5d73c4dfbcf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -523,6 +523,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -535,6 +537,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -844,6 +852,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index eed4c6b359c..8077c65e938 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -707,7 +707,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, uint32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--uN5gGi2rtCNSVbpC
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v5-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v1] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 2 +
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 160 insertions(+), 1 deletion(-)
26.9% doc/src/sgml/
29.1% src/backend/utils/activity/
12.3% src/backend/utils/adt/
9.7% src/include/catalog/
4.0% src/include/utils/
9.2% src/test/regress/expected/
7.3% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index fa78031ccbb..ef89683164f 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..7cec0c83071 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,13 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static int pgStatBackendXactCommit = 0;
+static int pgStatBackendXactRollback = 0;
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +266,33 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += pgStatBackendXactCommit;
+ shbackendent->stats.xact_rollback += pgStatBackendXactRollback;
+
+ pgStatBackendXactCommit = pgStatBackendXactRollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +317,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +336,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +441,22 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ pgStatBackendXactCommit++;
+ else
+ pgStatBackendXactRollback++;
+
+ backend_has_xactstats = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..4c9f6b0dcec 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--ZnbSmJtA6EgjuGCe--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v2] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 166 insertions(+), 1 deletion(-)
26.1% doc/src/sgml/
29.1% src/backend/utils/activity/
11.9% src/backend/utils/adt/
9.4% src/include/catalog/
3.9% src/include/utils/
3.3% src/include/
8.9% src/test/regress/expected/
7.1% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 3f4a27a736e..77b41e02b82 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..12ae9a8d321 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,23 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..d3491faaff2 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Xact statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--SywurcztURg2uk+W--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v3 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. A new function is called in AtEOXact_PgStat() to increment those
two new counters.
---
src/backend/utils/activity/pgstat_backend.c | 57 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
4 files changed, 69 insertions(+), 1 deletion(-)
78.9% src/backend/utils/activity/
11.2% src/include/utils/
9.8% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..bf164854c4b 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,20 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /* Count transaction commit or abort */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..9efc0e10ebf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
--
2.34.1
--u6+Li7FU79Mt/lVd
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v3-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v4 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..47ce61e5093 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -36,7 +36,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index b31f20d41bc..400a8c5d734 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -342,6 +342,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..0fdbaf79780 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -801,6 +809,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..b97184c6539 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,7 +616,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--jg9kqX3osLGO+8A7
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v4-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v5 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 7727fed3bda..42bfb9cd38f 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -37,7 +37,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -48,6 +48,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -261,6 +266,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -285,6 +318,10 @@ pgstat_flush_backend(bool nowait, uint32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -300,6 +337,9 @@ pgstat_flush_backend(bool nowait, uint32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index 933dcb5cae5..218ff5e653c 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -352,6 +352,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 8e3549c3752..5d73c4dfbcf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -523,6 +523,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -535,6 +537,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -844,6 +852,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index eed4c6b359c..8077c65e938 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -707,7 +707,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, uint32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--uN5gGi2rtCNSVbpC
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v5-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v1] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 2 +
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 160 insertions(+), 1 deletion(-)
26.9% doc/src/sgml/
29.1% src/backend/utils/activity/
12.3% src/backend/utils/adt/
9.7% src/include/catalog/
4.0% src/include/utils/
9.2% src/test/regress/expected/
7.3% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index fa78031ccbb..ef89683164f 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..7cec0c83071 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,13 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static int pgStatBackendXactCommit = 0;
+static int pgStatBackendXactRollback = 0;
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +266,33 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += pgStatBackendXactCommit;
+ shbackendent->stats.xact_rollback += pgStatBackendXactRollback;
+
+ pgStatBackendXactCommit = pgStatBackendXactRollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +317,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +336,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +441,22 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ pgStatBackendXactCommit++;
+ else
+ pgStatBackendXactRollback++;
+
+ backend_has_xactstats = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..4c9f6b0dcec 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--ZnbSmJtA6EgjuGCe--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v2] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 166 insertions(+), 1 deletion(-)
26.1% doc/src/sgml/
29.1% src/backend/utils/activity/
11.9% src/backend/utils/adt/
9.4% src/include/catalog/
3.9% src/include/utils/
3.3% src/include/
8.9% src/test/regress/expected/
7.1% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 3f4a27a736e..77b41e02b82 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..12ae9a8d321 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,23 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..d3491faaff2 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Xact statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--SywurcztURg2uk+W--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v3 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. A new function is called in AtEOXact_PgStat() to increment those
two new counters.
---
src/backend/utils/activity/pgstat_backend.c | 57 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
4 files changed, 69 insertions(+), 1 deletion(-)
78.9% src/backend/utils/activity/
11.2% src/include/utils/
9.8% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..bf164854c4b 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,20 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /* Count transaction commit or abort */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..9efc0e10ebf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
--
2.34.1
--u6+Li7FU79Mt/lVd
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v3-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v4 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..47ce61e5093 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -36,7 +36,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index b31f20d41bc..400a8c5d734 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -342,6 +342,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..0fdbaf79780 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -801,6 +809,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..b97184c6539 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,7 +616,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--jg9kqX3osLGO+8A7
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v4-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v5 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 7727fed3bda..42bfb9cd38f 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -37,7 +37,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -48,6 +48,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -261,6 +266,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -285,6 +318,10 @@ pgstat_flush_backend(bool nowait, uint32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -300,6 +337,9 @@ pgstat_flush_backend(bool nowait, uint32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index 933dcb5cae5..218ff5e653c 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -352,6 +352,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 8e3549c3752..5d73c4dfbcf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -523,6 +523,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -535,6 +537,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -844,6 +852,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index eed4c6b359c..8077c65e938 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -707,7 +707,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, uint32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--uN5gGi2rtCNSVbpC
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v5-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v1] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 2 +
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 160 insertions(+), 1 deletion(-)
26.9% doc/src/sgml/
29.1% src/backend/utils/activity/
12.3% src/backend/utils/adt/
9.7% src/include/catalog/
4.0% src/include/utils/
9.2% src/test/regress/expected/
7.3% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index fa78031ccbb..ef89683164f 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..7cec0c83071 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,13 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static int pgStatBackendXactCommit = 0;
+static int pgStatBackendXactRollback = 0;
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +266,33 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += pgStatBackendXactCommit;
+ shbackendent->stats.xact_rollback += pgStatBackendXactRollback;
+
+ pgStatBackendXactCommit = pgStatBackendXactRollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +317,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +336,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +441,22 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ pgStatBackendXactCommit++;
+ else
+ pgStatBackendXactRollback++;
+
+ backend_has_xactstats = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..4c9f6b0dcec 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--ZnbSmJtA6EgjuGCe--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v2] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 166 insertions(+), 1 deletion(-)
26.1% doc/src/sgml/
29.1% src/backend/utils/activity/
11.9% src/backend/utils/adt/
9.4% src/include/catalog/
3.9% src/include/utils/
3.3% src/include/
8.9% src/test/regress/expected/
7.1% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 3f4a27a736e..77b41e02b82 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..12ae9a8d321 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,23 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..d3491faaff2 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Xact statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--SywurcztURg2uk+W--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v3 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. A new function is called in AtEOXact_PgStat() to increment those
two new counters.
---
src/backend/utils/activity/pgstat_backend.c | 57 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
4 files changed, 69 insertions(+), 1 deletion(-)
78.9% src/backend/utils/activity/
11.2% src/include/utils/
9.8% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..bf164854c4b 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,20 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /* Count transaction commit or abort */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..9efc0e10ebf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
--
2.34.1
--u6+Li7FU79Mt/lVd
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v3-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v4 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..47ce61e5093 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -36,7 +36,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index b31f20d41bc..400a8c5d734 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -342,6 +342,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..0fdbaf79780 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -801,6 +809,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..b97184c6539 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,7 +616,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--jg9kqX3osLGO+8A7
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v4-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v5 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 7727fed3bda..42bfb9cd38f 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -37,7 +37,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -48,6 +48,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -261,6 +266,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -285,6 +318,10 @@ pgstat_flush_backend(bool nowait, uint32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -300,6 +337,9 @@ pgstat_flush_backend(bool nowait, uint32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index 933dcb5cae5..218ff5e653c 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -352,6 +352,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 8e3549c3752..5d73c4dfbcf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -523,6 +523,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -535,6 +537,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -844,6 +852,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index eed4c6b359c..8077c65e938 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -707,7 +707,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, uint32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--uN5gGi2rtCNSVbpC
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v5-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v1] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 2 +
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 160 insertions(+), 1 deletion(-)
26.9% doc/src/sgml/
29.1% src/backend/utils/activity/
12.3% src/backend/utils/adt/
9.7% src/include/catalog/
4.0% src/include/utils/
9.2% src/test/regress/expected/
7.3% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index fa78031ccbb..ef89683164f 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..7cec0c83071 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,13 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static int pgStatBackendXactCommit = 0;
+static int pgStatBackendXactRollback = 0;
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +266,33 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += pgStatBackendXactCommit;
+ shbackendent->stats.xact_rollback += pgStatBackendXactRollback;
+
+ pgStatBackendXactCommit = pgStatBackendXactRollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +317,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +336,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +441,22 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ pgStatBackendXactCommit++;
+ else
+ pgStatBackendXactRollback++;
+
+ backend_has_xactstats = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..4c9f6b0dcec 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--ZnbSmJtA6EgjuGCe--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v2] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 166 insertions(+), 1 deletion(-)
26.1% doc/src/sgml/
29.1% src/backend/utils/activity/
11.9% src/backend/utils/adt/
9.4% src/include/catalog/
3.9% src/include/utils/
3.3% src/include/
8.9% src/test/regress/expected/
7.1% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 3f4a27a736e..77b41e02b82 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..12ae9a8d321 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,23 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..d3491faaff2 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Xact statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--SywurcztURg2uk+W--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v3 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. A new function is called in AtEOXact_PgStat() to increment those
two new counters.
---
src/backend/utils/activity/pgstat_backend.c | 57 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
4 files changed, 69 insertions(+), 1 deletion(-)
78.9% src/backend/utils/activity/
11.2% src/include/utils/
9.8% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..bf164854c4b 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,20 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /* Count transaction commit or abort */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..9efc0e10ebf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
--
2.34.1
--u6+Li7FU79Mt/lVd
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v3-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v4 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..47ce61e5093 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -36,7 +36,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index b31f20d41bc..400a8c5d734 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -342,6 +342,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..0fdbaf79780 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -801,6 +809,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..b97184c6539 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,7 +616,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--jg9kqX3osLGO+8A7
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v4-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v5 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 7727fed3bda..42bfb9cd38f 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -37,7 +37,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -48,6 +48,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -261,6 +266,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -285,6 +318,10 @@ pgstat_flush_backend(bool nowait, uint32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -300,6 +337,9 @@ pgstat_flush_backend(bool nowait, uint32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index 933dcb5cae5..218ff5e653c 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -352,6 +352,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 8e3549c3752..5d73c4dfbcf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -523,6 +523,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -535,6 +537,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -844,6 +852,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index eed4c6b359c..8077c65e938 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -707,7 +707,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, uint32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--uN5gGi2rtCNSVbpC
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v5-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v5 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 7727fed3bda..42bfb9cd38f 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -37,7 +37,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -48,6 +48,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -261,6 +266,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -285,6 +318,10 @@ pgstat_flush_backend(bool nowait, uint32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -300,6 +337,9 @@ pgstat_flush_backend(bool nowait, uint32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index 933dcb5cae5..218ff5e653c 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -352,6 +352,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 8e3549c3752..5d73c4dfbcf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -523,6 +523,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -535,6 +537,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -844,6 +852,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index eed4c6b359c..8077c65e938 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -707,7 +707,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, uint32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--uN5gGi2rtCNSVbpC
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v5-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v1] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 2 +
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 160 insertions(+), 1 deletion(-)
26.9% doc/src/sgml/
29.1% src/backend/utils/activity/
12.3% src/backend/utils/adt/
9.7% src/include/catalog/
4.0% src/include/utils/
9.2% src/test/regress/expected/
7.3% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index fa78031ccbb..ef89683164f 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..7cec0c83071 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,13 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static int pgStatBackendXactCommit = 0;
+static int pgStatBackendXactRollback = 0;
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +266,33 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += pgStatBackendXactCommit;
+ shbackendent->stats.xact_rollback += pgStatBackendXactRollback;
+
+ pgStatBackendXactCommit = pgStatBackendXactRollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +317,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +336,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +441,22 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ pgStatBackendXactCommit++;
+ else
+ pgStatBackendXactRollback++;
+
+ backend_has_xactstats = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..4c9f6b0dcec 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--ZnbSmJtA6EgjuGCe--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v1] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 2 +
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 160 insertions(+), 1 deletion(-)
26.9% doc/src/sgml/
29.1% src/backend/utils/activity/
12.3% src/backend/utils/adt/
9.7% src/include/catalog/
4.0% src/include/utils/
9.2% src/test/regress/expected/
7.3% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index fa78031ccbb..ef89683164f 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..7cec0c83071 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,13 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static int pgStatBackendXactCommit = 0;
+static int pgStatBackendXactRollback = 0;
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +266,33 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += pgStatBackendXactCommit;
+ shbackendent->stats.xact_rollback += pgStatBackendXactRollback;
+
+ pgStatBackendXactCommit = pgStatBackendXactRollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +317,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +336,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +441,22 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ pgStatBackendXactCommit++;
+ else
+ pgStatBackendXactRollback++;
+
+ backend_has_xactstats = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..4c9f6b0dcec 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--ZnbSmJtA6EgjuGCe--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v2] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 166 insertions(+), 1 deletion(-)
26.1% doc/src/sgml/
29.1% src/backend/utils/activity/
11.9% src/backend/utils/adt/
9.4% src/include/catalog/
3.9% src/include/utils/
3.3% src/include/
8.9% src/test/regress/expected/
7.1% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 3f4a27a736e..77b41e02b82 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..12ae9a8d321 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,23 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..d3491faaff2 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Xact statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--SywurcztURg2uk+W--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v3 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. A new function is called in AtEOXact_PgStat() to increment those
two new counters.
---
src/backend/utils/activity/pgstat_backend.c | 57 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
4 files changed, 69 insertions(+), 1 deletion(-)
78.9% src/backend/utils/activity/
11.2% src/include/utils/
9.8% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..bf164854c4b 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,20 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /* Count transaction commit or abort */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..9efc0e10ebf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
--
2.34.1
--u6+Li7FU79Mt/lVd
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v3-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v4 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..47ce61e5093 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -36,7 +36,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index b31f20d41bc..400a8c5d734 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -342,6 +342,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..0fdbaf79780 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -801,6 +809,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..b97184c6539 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,7 +616,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--jg9kqX3osLGO+8A7
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v4-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v5 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 7727fed3bda..42bfb9cd38f 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -37,7 +37,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -48,6 +48,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -261,6 +266,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -285,6 +318,10 @@ pgstat_flush_backend(bool nowait, uint32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -300,6 +337,9 @@ pgstat_flush_backend(bool nowait, uint32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index 933dcb5cae5..218ff5e653c 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -352,6 +352,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 8e3549c3752..5d73c4dfbcf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -523,6 +523,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -535,6 +537,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -844,6 +852,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index eed4c6b359c..8077c65e938 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -707,7 +707,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, uint32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--uN5gGi2rtCNSVbpC
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v5-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v1] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 2 +
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 160 insertions(+), 1 deletion(-)
26.9% doc/src/sgml/
29.1% src/backend/utils/activity/
12.3% src/backend/utils/adt/
9.7% src/include/catalog/
4.0% src/include/utils/
9.2% src/test/regress/expected/
7.3% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index fa78031ccbb..ef89683164f 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..7cec0c83071 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,13 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static int pgStatBackendXactCommit = 0;
+static int pgStatBackendXactRollback = 0;
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +266,33 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += pgStatBackendXactCommit;
+ shbackendent->stats.xact_rollback += pgStatBackendXactRollback;
+
+ pgStatBackendXactCommit = pgStatBackendXactRollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +317,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +336,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +441,22 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ pgStatBackendXactCommit++;
+ else
+ pgStatBackendXactRollback++;
+
+ backend_has_xactstats = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..4c9f6b0dcec 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--ZnbSmJtA6EgjuGCe--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v2] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 166 insertions(+), 1 deletion(-)
26.1% doc/src/sgml/
29.1% src/backend/utils/activity/
11.9% src/backend/utils/adt/
9.4% src/include/catalog/
3.9% src/include/utils/
3.3% src/include/
8.9% src/test/regress/expected/
7.1% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 3f4a27a736e..77b41e02b82 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..12ae9a8d321 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,23 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..d3491faaff2 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Xact statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--SywurcztURg2uk+W--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v3 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. A new function is called in AtEOXact_PgStat() to increment those
two new counters.
---
src/backend/utils/activity/pgstat_backend.c | 57 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
4 files changed, 69 insertions(+), 1 deletion(-)
78.9% src/backend/utils/activity/
11.2% src/include/utils/
9.8% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..bf164854c4b 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,20 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /* Count transaction commit or abort */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..9efc0e10ebf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
--
2.34.1
--u6+Li7FU79Mt/lVd
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v3-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v4 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..47ce61e5093 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -36,7 +36,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index b31f20d41bc..400a8c5d734 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -342,6 +342,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..0fdbaf79780 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -801,6 +809,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..b97184c6539 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,7 +616,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--jg9kqX3osLGO+8A7
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v4-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v5 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 7727fed3bda..42bfb9cd38f 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -37,7 +37,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -48,6 +48,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -261,6 +266,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -285,6 +318,10 @@ pgstat_flush_backend(bool nowait, uint32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -300,6 +337,9 @@ pgstat_flush_backend(bool nowait, uint32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index 933dcb5cae5..218ff5e653c 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -352,6 +352,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 8e3549c3752..5d73c4dfbcf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -523,6 +523,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -535,6 +537,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -844,6 +852,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index eed4c6b359c..8077c65e938 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -707,7 +707,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, uint32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--uN5gGi2rtCNSVbpC
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v5-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v1] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 2 +
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 160 insertions(+), 1 deletion(-)
26.9% doc/src/sgml/
29.1% src/backend/utils/activity/
12.3% src/backend/utils/adt/
9.7% src/include/catalog/
4.0% src/include/utils/
9.2% src/test/regress/expected/
7.3% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index fa78031ccbb..ef89683164f 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..7cec0c83071 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,13 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static int pgStatBackendXactCommit = 0;
+static int pgStatBackendXactRollback = 0;
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +266,33 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += pgStatBackendXactCommit;
+ shbackendent->stats.xact_rollback += pgStatBackendXactRollback;
+
+ pgStatBackendXactCommit = pgStatBackendXactRollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +317,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +336,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +441,22 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ pgStatBackendXactCommit++;
+ else
+ pgStatBackendXactRollback++;
+
+ backend_has_xactstats = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..4c9f6b0dcec 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--ZnbSmJtA6EgjuGCe--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v2] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 166 insertions(+), 1 deletion(-)
26.1% doc/src/sgml/
29.1% src/backend/utils/activity/
11.9% src/backend/utils/adt/
9.4% src/include/catalog/
3.9% src/include/utils/
3.3% src/include/
8.9% src/test/regress/expected/
7.1% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 3f4a27a736e..77b41e02b82 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..12ae9a8d321 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,23 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..d3491faaff2 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Xact statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--SywurcztURg2uk+W--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v3 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. A new function is called in AtEOXact_PgStat() to increment those
two new counters.
---
src/backend/utils/activity/pgstat_backend.c | 57 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
4 files changed, 69 insertions(+), 1 deletion(-)
78.9% src/backend/utils/activity/
11.2% src/include/utils/
9.8% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..bf164854c4b 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,20 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /* Count transaction commit or abort */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..9efc0e10ebf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
--
2.34.1
--u6+Li7FU79Mt/lVd
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v3-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v4 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..47ce61e5093 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -36,7 +36,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index b31f20d41bc..400a8c5d734 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -342,6 +342,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..0fdbaf79780 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -801,6 +809,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..b97184c6539 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,7 +616,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--jg9kqX3osLGO+8A7
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v4-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v5 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 7727fed3bda..42bfb9cd38f 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -37,7 +37,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -48,6 +48,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -261,6 +266,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -285,6 +318,10 @@ pgstat_flush_backend(bool nowait, uint32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -300,6 +337,9 @@ pgstat_flush_backend(bool nowait, uint32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index 933dcb5cae5..218ff5e653c 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -352,6 +352,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 8e3549c3752..5d73c4dfbcf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -523,6 +523,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -535,6 +537,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -844,6 +852,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index eed4c6b359c..8077c65e938 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -707,7 +707,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, uint32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--uN5gGi2rtCNSVbpC
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v5-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v2] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 166 insertions(+), 1 deletion(-)
26.1% doc/src/sgml/
29.1% src/backend/utils/activity/
11.9% src/backend/utils/adt/
9.4% src/include/catalog/
3.9% src/include/utils/
3.3% src/include/
8.9% src/test/regress/expected/
7.1% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 3f4a27a736e..77b41e02b82 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..12ae9a8d321 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,23 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..d3491faaff2 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Xact statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--SywurcztURg2uk+W--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v3 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. A new function is called in AtEOXact_PgStat() to increment those
two new counters.
---
src/backend/utils/activity/pgstat_backend.c | 57 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
4 files changed, 69 insertions(+), 1 deletion(-)
78.9% src/backend/utils/activity/
11.2% src/include/utils/
9.8% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..bf164854c4b 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,20 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /* Count transaction commit or abort */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..9efc0e10ebf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
--
2.34.1
--u6+Li7FU79Mt/lVd
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v3-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v1] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 2 +
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 160 insertions(+), 1 deletion(-)
26.9% doc/src/sgml/
29.1% src/backend/utils/activity/
12.3% src/backend/utils/adt/
9.7% src/include/catalog/
4.0% src/include/utils/
9.2% src/test/regress/expected/
7.3% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index fa78031ccbb..ef89683164f 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..7cec0c83071 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,13 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static int pgStatBackendXactCommit = 0;
+static int pgStatBackendXactRollback = 0;
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +266,33 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += pgStatBackendXactCommit;
+ shbackendent->stats.xact_rollback += pgStatBackendXactRollback;
+
+ pgStatBackendXactCommit = pgStatBackendXactRollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +317,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +336,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +441,22 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ pgStatBackendXactCommit++;
+ else
+ pgStatBackendXactRollback++;
+
+ backend_has_xactstats = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..4c9f6b0dcec 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--ZnbSmJtA6EgjuGCe--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v2] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 166 insertions(+), 1 deletion(-)
26.1% doc/src/sgml/
29.1% src/backend/utils/activity/
11.9% src/backend/utils/adt/
9.4% src/include/catalog/
3.9% src/include/utils/
3.3% src/include/
8.9% src/test/regress/expected/
7.1% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 3f4a27a736e..77b41e02b82 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..12ae9a8d321 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,23 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..d3491faaff2 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Xact statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--SywurcztURg2uk+W--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v3 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. A new function is called in AtEOXact_PgStat() to increment those
two new counters.
---
src/backend/utils/activity/pgstat_backend.c | 57 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
4 files changed, 69 insertions(+), 1 deletion(-)
78.9% src/backend/utils/activity/
11.2% src/include/utils/
9.8% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..bf164854c4b 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,20 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /* Count transaction commit or abort */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..9efc0e10ebf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
--
2.34.1
--u6+Li7FU79Mt/lVd
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v3-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v4 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..47ce61e5093 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -36,7 +36,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index b31f20d41bc..400a8c5d734 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -342,6 +342,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..0fdbaf79780 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -801,6 +809,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..b97184c6539 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,7 +616,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--jg9kqX3osLGO+8A7
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v4-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v5 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 7727fed3bda..42bfb9cd38f 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -37,7 +37,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -48,6 +48,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -261,6 +266,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -285,6 +318,10 @@ pgstat_flush_backend(bool nowait, uint32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -300,6 +337,9 @@ pgstat_flush_backend(bool nowait, uint32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index 933dcb5cae5..218ff5e653c 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -352,6 +352,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 8e3549c3752..5d73c4dfbcf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -523,6 +523,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -535,6 +537,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -844,6 +852,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index eed4c6b359c..8077c65e938 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -707,7 +707,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, uint32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--uN5gGi2rtCNSVbpC
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v5-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v5 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 7727fed3bda..42bfb9cd38f 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -37,7 +37,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -48,6 +48,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -261,6 +266,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -285,6 +318,10 @@ pgstat_flush_backend(bool nowait, uint32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -300,6 +337,9 @@ pgstat_flush_backend(bool nowait, uint32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index 933dcb5cae5..218ff5e653c 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -352,6 +352,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 8e3549c3752..5d73c4dfbcf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -523,6 +523,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -535,6 +537,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -844,6 +852,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index eed4c6b359c..8077c65e938 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -707,7 +707,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, uint32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--uN5gGi2rtCNSVbpC
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v5-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v1] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 2 +
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 160 insertions(+), 1 deletion(-)
26.9% doc/src/sgml/
29.1% src/backend/utils/activity/
12.3% src/backend/utils/adt/
9.7% src/include/catalog/
4.0% src/include/utils/
9.2% src/test/regress/expected/
7.3% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index fa78031ccbb..ef89683164f 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..7cec0c83071 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,13 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static int pgStatBackendXactCommit = 0;
+static int pgStatBackendXactRollback = 0;
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +266,33 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += pgStatBackendXactCommit;
+ shbackendent->stats.xact_rollback += pgStatBackendXactRollback;
+
+ pgStatBackendXactCommit = pgStatBackendXactRollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +317,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +336,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +441,22 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ pgStatBackendXactCommit++;
+ else
+ pgStatBackendXactRollback++;
+
+ backend_has_xactstats = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..4c9f6b0dcec 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--ZnbSmJtA6EgjuGCe--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v2] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 166 insertions(+), 1 deletion(-)
26.1% doc/src/sgml/
29.1% src/backend/utils/activity/
11.9% src/backend/utils/adt/
9.4% src/include/catalog/
3.9% src/include/utils/
3.3% src/include/
8.9% src/test/regress/expected/
7.1% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 3f4a27a736e..77b41e02b82 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..12ae9a8d321 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,23 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..d3491faaff2 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Xact statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--SywurcztURg2uk+W--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v3 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. A new function is called in AtEOXact_PgStat() to increment those
two new counters.
---
src/backend/utils/activity/pgstat_backend.c | 57 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
4 files changed, 69 insertions(+), 1 deletion(-)
78.9% src/backend/utils/activity/
11.2% src/include/utils/
9.8% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..bf164854c4b 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,20 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /* Count transaction commit or abort */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..9efc0e10ebf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
--
2.34.1
--u6+Li7FU79Mt/lVd
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v3-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v4 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..47ce61e5093 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -36,7 +36,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index b31f20d41bc..400a8c5d734 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -342,6 +342,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..0fdbaf79780 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -801,6 +809,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..b97184c6539 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,7 +616,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--jg9kqX3osLGO+8A7
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v4-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v5 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 7727fed3bda..42bfb9cd38f 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -37,7 +37,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -48,6 +48,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -261,6 +266,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -285,6 +318,10 @@ pgstat_flush_backend(bool nowait, uint32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -300,6 +337,9 @@ pgstat_flush_backend(bool nowait, uint32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index 933dcb5cae5..218ff5e653c 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -352,6 +352,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 8e3549c3752..5d73c4dfbcf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -523,6 +523,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -535,6 +537,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -844,6 +852,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index eed4c6b359c..8077c65e938 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -707,7 +707,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, uint32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--uN5gGi2rtCNSVbpC
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v5-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v2] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 166 insertions(+), 1 deletion(-)
26.1% doc/src/sgml/
29.1% src/backend/utils/activity/
11.9% src/backend/utils/adt/
9.4% src/include/catalog/
3.9% src/include/utils/
3.3% src/include/
8.9% src/test/regress/expected/
7.1% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 3f4a27a736e..77b41e02b82 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..12ae9a8d321 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,23 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..d3491faaff2 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Xact statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--SywurcztURg2uk+W--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v1] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 2 +
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 160 insertions(+), 1 deletion(-)
26.9% doc/src/sgml/
29.1% src/backend/utils/activity/
12.3% src/backend/utils/adt/
9.7% src/include/catalog/
4.0% src/include/utils/
9.2% src/test/regress/expected/
7.3% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index fa78031ccbb..ef89683164f 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..7cec0c83071 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,13 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static int pgStatBackendXactCommit = 0;
+static int pgStatBackendXactRollback = 0;
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +266,33 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += pgStatBackendXactCommit;
+ shbackendent->stats.xact_rollback += pgStatBackendXactRollback;
+
+ pgStatBackendXactCommit = pgStatBackendXactRollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +317,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +336,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +441,22 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ pgStatBackendXactCommit++;
+ else
+ pgStatBackendXactRollback++;
+
+ backend_has_xactstats = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..4c9f6b0dcec 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--ZnbSmJtA6EgjuGCe--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v2] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 166 insertions(+), 1 deletion(-)
26.1% doc/src/sgml/
29.1% src/backend/utils/activity/
11.9% src/backend/utils/adt/
9.4% src/include/catalog/
3.9% src/include/utils/
3.3% src/include/
8.9% src/test/regress/expected/
7.1% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 3f4a27a736e..77b41e02b82 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..12ae9a8d321 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,23 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..d3491faaff2 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Xact statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--SywurcztURg2uk+W--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v3 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. A new function is called in AtEOXact_PgStat() to increment those
two new counters.
---
src/backend/utils/activity/pgstat_backend.c | 57 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
4 files changed, 69 insertions(+), 1 deletion(-)
78.9% src/backend/utils/activity/
11.2% src/include/utils/
9.8% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..bf164854c4b 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,20 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /* Count transaction commit or abort */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..9efc0e10ebf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
--
2.34.1
--u6+Li7FU79Mt/lVd
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v3-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v4 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..47ce61e5093 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -36,7 +36,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index b31f20d41bc..400a8c5d734 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -342,6 +342,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..0fdbaf79780 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -801,6 +809,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..b97184c6539 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,7 +616,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--jg9kqX3osLGO+8A7
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v4-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v5 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 7727fed3bda..42bfb9cd38f 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -37,7 +37,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -48,6 +48,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -261,6 +266,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -285,6 +318,10 @@ pgstat_flush_backend(bool nowait, uint32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -300,6 +337,9 @@ pgstat_flush_backend(bool nowait, uint32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index 933dcb5cae5..218ff5e653c 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -352,6 +352,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 8e3549c3752..5d73c4dfbcf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -523,6 +523,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -535,6 +537,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -844,6 +852,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index eed4c6b359c..8077c65e938 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -707,7 +707,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, uint32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--uN5gGi2rtCNSVbpC
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v5-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v2] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 166 insertions(+), 1 deletion(-)
26.1% doc/src/sgml/
29.1% src/backend/utils/activity/
11.9% src/backend/utils/adt/
9.4% src/include/catalog/
3.9% src/include/utils/
3.3% src/include/
8.9% src/test/regress/expected/
7.1% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 3f4a27a736e..77b41e02b82 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..12ae9a8d321 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,23 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..d3491faaff2 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Xact statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--SywurcztURg2uk+W--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v4 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..47ce61e5093 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -36,7 +36,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index b31f20d41bc..400a8c5d734 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -342,6 +342,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..0fdbaf79780 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -801,6 +809,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..b97184c6539 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,7 +616,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--jg9kqX3osLGO+8A7
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v4-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v1] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 2 +
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 160 insertions(+), 1 deletion(-)
26.9% doc/src/sgml/
29.1% src/backend/utils/activity/
12.3% src/backend/utils/adt/
9.7% src/include/catalog/
4.0% src/include/utils/
9.2% src/test/regress/expected/
7.3% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index fa78031ccbb..ef89683164f 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..7cec0c83071 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,13 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static int pgStatBackendXactCommit = 0;
+static int pgStatBackendXactRollback = 0;
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +266,33 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += pgStatBackendXactCommit;
+ shbackendent->stats.xact_rollback += pgStatBackendXactRollback;
+
+ pgStatBackendXactCommit = pgStatBackendXactRollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +317,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +336,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +441,22 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ pgStatBackendXactCommit++;
+ else
+ pgStatBackendXactRollback++;
+
+ backend_has_xactstats = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..4c9f6b0dcec 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--ZnbSmJtA6EgjuGCe--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v3 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. A new function is called in AtEOXact_PgStat() to increment those
two new counters.
---
src/backend/utils/activity/pgstat_backend.c | 57 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
4 files changed, 69 insertions(+), 1 deletion(-)
78.9% src/backend/utils/activity/
11.2% src/include/utils/
9.8% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..bf164854c4b 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,20 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /* Count transaction commit or abort */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..9efc0e10ebf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
--
2.34.1
--u6+Li7FU79Mt/lVd
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v3-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v4 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..47ce61e5093 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -36,7 +36,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index b31f20d41bc..400a8c5d734 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -342,6 +342,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..0fdbaf79780 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -801,6 +809,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..b97184c6539 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,7 +616,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--jg9kqX3osLGO+8A7
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v4-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v5 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 7727fed3bda..42bfb9cd38f 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -37,7 +37,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -48,6 +48,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -261,6 +266,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -285,6 +318,10 @@ pgstat_flush_backend(bool nowait, uint32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -300,6 +337,9 @@ pgstat_flush_backend(bool nowait, uint32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index 933dcb5cae5..218ff5e653c 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -352,6 +352,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 8e3549c3752..5d73c4dfbcf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -523,6 +523,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -535,6 +537,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -844,6 +852,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index eed4c6b359c..8077c65e938 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -707,7 +707,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, uint32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--uN5gGi2rtCNSVbpC
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v5-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v1] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 2 +
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 160 insertions(+), 1 deletion(-)
26.9% doc/src/sgml/
29.1% src/backend/utils/activity/
12.3% src/backend/utils/adt/
9.7% src/include/catalog/
4.0% src/include/utils/
9.2% src/test/regress/expected/
7.3% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index fa78031ccbb..ef89683164f 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..7cec0c83071 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,13 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static int pgStatBackendXactCommit = 0;
+static int pgStatBackendXactRollback = 0;
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +266,33 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += pgStatBackendXactCommit;
+ shbackendent->stats.xact_rollback += pgStatBackendXactRollback;
+
+ pgStatBackendXactCommit = pgStatBackendXactRollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +317,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +336,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +441,22 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ pgStatBackendXactCommit++;
+ else
+ pgStatBackendXactRollback++;
+
+ backend_has_xactstats = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..4c9f6b0dcec 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--ZnbSmJtA6EgjuGCe--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v2] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 166 insertions(+), 1 deletion(-)
26.1% doc/src/sgml/
29.1% src/backend/utils/activity/
11.9% src/backend/utils/adt/
9.4% src/include/catalog/
3.9% src/include/utils/
3.3% src/include/
8.9% src/test/regress/expected/
7.1% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 3f4a27a736e..77b41e02b82 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..12ae9a8d321 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,23 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..d3491faaff2 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Xact statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--SywurcztURg2uk+W--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v3 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. A new function is called in AtEOXact_PgStat() to increment those
two new counters.
---
src/backend/utils/activity/pgstat_backend.c | 57 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
4 files changed, 69 insertions(+), 1 deletion(-)
78.9% src/backend/utils/activity/
11.2% src/include/utils/
9.8% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..bf164854c4b 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,20 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /* Count transaction commit or abort */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..9efc0e10ebf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
--
2.34.1
--u6+Li7FU79Mt/lVd
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v3-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v4 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..47ce61e5093 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -36,7 +36,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index b31f20d41bc..400a8c5d734 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -342,6 +342,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..0fdbaf79780 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -801,6 +809,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..b97184c6539 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,7 +616,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--jg9kqX3osLGO+8A7
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v4-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v5 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 7727fed3bda..42bfb9cd38f 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -37,7 +37,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -48,6 +48,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -261,6 +266,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -285,6 +318,10 @@ pgstat_flush_backend(bool nowait, uint32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -300,6 +337,9 @@ pgstat_flush_backend(bool nowait, uint32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index 933dcb5cae5..218ff5e653c 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -352,6 +352,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 8e3549c3752..5d73c4dfbcf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -523,6 +523,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -535,6 +537,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -844,6 +852,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index eed4c6b359c..8077c65e938 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -707,7 +707,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, uint32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--uN5gGi2rtCNSVbpC
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v5-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v1] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 2 +
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 160 insertions(+), 1 deletion(-)
26.9% doc/src/sgml/
29.1% src/backend/utils/activity/
12.3% src/backend/utils/adt/
9.7% src/include/catalog/
4.0% src/include/utils/
9.2% src/test/regress/expected/
7.3% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index fa78031ccbb..ef89683164f 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..7cec0c83071 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,13 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static int pgStatBackendXactCommit = 0;
+static int pgStatBackendXactRollback = 0;
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +266,33 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += pgStatBackendXactCommit;
+ shbackendent->stats.xact_rollback += pgStatBackendXactRollback;
+
+ pgStatBackendXactCommit = pgStatBackendXactRollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +317,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +336,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +441,22 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ pgStatBackendXactCommit++;
+ else
+ pgStatBackendXactRollback++;
+
+ backend_has_xactstats = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..4c9f6b0dcec 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--ZnbSmJtA6EgjuGCe--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v2] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 166 insertions(+), 1 deletion(-)
26.1% doc/src/sgml/
29.1% src/backend/utils/activity/
11.9% src/backend/utils/adt/
9.4% src/include/catalog/
3.9% src/include/utils/
3.3% src/include/
8.9% src/test/regress/expected/
7.1% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 3f4a27a736e..77b41e02b82 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..12ae9a8d321 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,23 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..d3491faaff2 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Xact statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--SywurcztURg2uk+W--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v3 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. A new function is called in AtEOXact_PgStat() to increment those
two new counters.
---
src/backend/utils/activity/pgstat_backend.c | 57 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
4 files changed, 69 insertions(+), 1 deletion(-)
78.9% src/backend/utils/activity/
11.2% src/include/utils/
9.8% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..bf164854c4b 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,20 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /* Count transaction commit or abort */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..9efc0e10ebf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
--
2.34.1
--u6+Li7FU79Mt/lVd
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v3-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v4 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..47ce61e5093 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -36,7 +36,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index b31f20d41bc..400a8c5d734 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -342,6 +342,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..0fdbaf79780 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -801,6 +809,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..b97184c6539 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,7 +616,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--jg9kqX3osLGO+8A7
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v4-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v5 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 7727fed3bda..42bfb9cd38f 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -37,7 +37,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -48,6 +48,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -261,6 +266,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -285,6 +318,10 @@ pgstat_flush_backend(bool nowait, uint32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -300,6 +337,9 @@ pgstat_flush_backend(bool nowait, uint32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index 933dcb5cae5..218ff5e653c 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -352,6 +352,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 8e3549c3752..5d73c4dfbcf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -523,6 +523,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -535,6 +537,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -844,6 +852,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index eed4c6b359c..8077c65e938 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -707,7 +707,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, uint32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--uN5gGi2rtCNSVbpC
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v5-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v1] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 2 +
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 160 insertions(+), 1 deletion(-)
26.9% doc/src/sgml/
29.1% src/backend/utils/activity/
12.3% src/backend/utils/adt/
9.7% src/include/catalog/
4.0% src/include/utils/
9.2% src/test/regress/expected/
7.3% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index fa78031ccbb..ef89683164f 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..7cec0c83071 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,13 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static int pgStatBackendXactCommit = 0;
+static int pgStatBackendXactRollback = 0;
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +266,33 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += pgStatBackendXactCommit;
+ shbackendent->stats.xact_rollback += pgStatBackendXactRollback;
+
+ pgStatBackendXactCommit = pgStatBackendXactRollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +317,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +336,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +441,22 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ pgStatBackendXactCommit++;
+ else
+ pgStatBackendXactRollback++;
+
+ backend_has_xactstats = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..4c9f6b0dcec 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--ZnbSmJtA6EgjuGCe--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v2] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 166 insertions(+), 1 deletion(-)
26.1% doc/src/sgml/
29.1% src/backend/utils/activity/
11.9% src/backend/utils/adt/
9.4% src/include/catalog/
3.9% src/include/utils/
3.3% src/include/
8.9% src/test/regress/expected/
7.1% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 3f4a27a736e..77b41e02b82 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..12ae9a8d321 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,23 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..d3491faaff2 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Xact statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--SywurcztURg2uk+W--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v3 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. A new function is called in AtEOXact_PgStat() to increment those
two new counters.
---
src/backend/utils/activity/pgstat_backend.c | 57 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
4 files changed, 69 insertions(+), 1 deletion(-)
78.9% src/backend/utils/activity/
11.2% src/include/utils/
9.8% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..bf164854c4b 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,20 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /* Count transaction commit or abort */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..9efc0e10ebf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
--
2.34.1
--u6+Li7FU79Mt/lVd
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v3-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v4 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..47ce61e5093 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -36,7 +36,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index b31f20d41bc..400a8c5d734 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -342,6 +342,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..0fdbaf79780 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -801,6 +809,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..b97184c6539 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,7 +616,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--jg9kqX3osLGO+8A7
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v4-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v5 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 7727fed3bda..42bfb9cd38f 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -37,7 +37,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -48,6 +48,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -261,6 +266,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -285,6 +318,10 @@ pgstat_flush_backend(bool nowait, uint32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -300,6 +337,9 @@ pgstat_flush_backend(bool nowait, uint32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index 933dcb5cae5..218ff5e653c 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -352,6 +352,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 8e3549c3752..5d73c4dfbcf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -523,6 +523,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -535,6 +537,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -844,6 +852,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index eed4c6b359c..8077c65e938 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -707,7 +707,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, uint32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--uN5gGi2rtCNSVbpC
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v5-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v1] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 2 +
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 160 insertions(+), 1 deletion(-)
26.9% doc/src/sgml/
29.1% src/backend/utils/activity/
12.3% src/backend/utils/adt/
9.7% src/include/catalog/
4.0% src/include/utils/
9.2% src/test/regress/expected/
7.3% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index fa78031ccbb..ef89683164f 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..7cec0c83071 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,13 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static int pgStatBackendXactCommit = 0;
+static int pgStatBackendXactRollback = 0;
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +266,33 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += pgStatBackendXactCommit;
+ shbackendent->stats.xact_rollback += pgStatBackendXactRollback;
+
+ pgStatBackendXactCommit = pgStatBackendXactRollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +317,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +336,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +441,22 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ pgStatBackendXactCommit++;
+ else
+ pgStatBackendXactRollback++;
+
+ backend_has_xactstats = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..4c9f6b0dcec 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--ZnbSmJtA6EgjuGCe--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v2] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 166 insertions(+), 1 deletion(-)
26.1% doc/src/sgml/
29.1% src/backend/utils/activity/
11.9% src/backend/utils/adt/
9.4% src/include/catalog/
3.9% src/include/utils/
3.3% src/include/
8.9% src/test/regress/expected/
7.1% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 3f4a27a736e..77b41e02b82 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..12ae9a8d321 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,23 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..d3491faaff2 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Xact statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--SywurcztURg2uk+W--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v3 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. A new function is called in AtEOXact_PgStat() to increment those
two new counters.
---
src/backend/utils/activity/pgstat_backend.c | 57 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
4 files changed, 69 insertions(+), 1 deletion(-)
78.9% src/backend/utils/activity/
11.2% src/include/utils/
9.8% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..bf164854c4b 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,20 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /* Count transaction commit or abort */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..9efc0e10ebf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
--
2.34.1
--u6+Li7FU79Mt/lVd
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v3-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v1] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 2 +
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 160 insertions(+), 1 deletion(-)
26.9% doc/src/sgml/
29.1% src/backend/utils/activity/
12.3% src/backend/utils/adt/
9.7% src/include/catalog/
4.0% src/include/utils/
9.2% src/test/regress/expected/
7.3% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index fa78031ccbb..ef89683164f 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..7cec0c83071 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,13 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static int pgStatBackendXactCommit = 0;
+static int pgStatBackendXactRollback = 0;
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +266,33 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += pgStatBackendXactCommit;
+ shbackendent->stats.xact_rollback += pgStatBackendXactRollback;
+
+ pgStatBackendXactCommit = pgStatBackendXactRollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +317,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +336,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +441,22 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ pgStatBackendXactCommit++;
+ else
+ pgStatBackendXactRollback++;
+
+ backend_has_xactstats = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..4c9f6b0dcec 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--ZnbSmJtA6EgjuGCe--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v2] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 166 insertions(+), 1 deletion(-)
26.1% doc/src/sgml/
29.1% src/backend/utils/activity/
11.9% src/backend/utils/adt/
9.4% src/include/catalog/
3.9% src/include/utils/
3.3% src/include/
8.9% src/test/regress/expected/
7.1% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 3f4a27a736e..77b41e02b82 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..12ae9a8d321 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,23 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..d3491faaff2 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Xact statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--SywurcztURg2uk+W--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v3 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. A new function is called in AtEOXact_PgStat() to increment those
two new counters.
---
src/backend/utils/activity/pgstat_backend.c | 57 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
4 files changed, 69 insertions(+), 1 deletion(-)
78.9% src/backend/utils/activity/
11.2% src/include/utils/
9.8% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..bf164854c4b 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,20 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /* Count transaction commit or abort */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..9efc0e10ebf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
--
2.34.1
--u6+Li7FU79Mt/lVd
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v3-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v4 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..47ce61e5093 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -36,7 +36,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index b31f20d41bc..400a8c5d734 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -342,6 +342,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..0fdbaf79780 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -801,6 +809,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..b97184c6539 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,7 +616,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--jg9kqX3osLGO+8A7
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v4-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v5 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 7727fed3bda..42bfb9cd38f 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -37,7 +37,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -48,6 +48,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -261,6 +266,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -285,6 +318,10 @@ pgstat_flush_backend(bool nowait, uint32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -300,6 +337,9 @@ pgstat_flush_backend(bool nowait, uint32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index 933dcb5cae5..218ff5e653c 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -352,6 +352,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 8e3549c3752..5d73c4dfbcf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -523,6 +523,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -535,6 +537,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -844,6 +852,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index eed4c6b359c..8077c65e938 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -707,7 +707,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, uint32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--uN5gGi2rtCNSVbpC
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v5-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v1] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 2 +
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 160 insertions(+), 1 deletion(-)
26.9% doc/src/sgml/
29.1% src/backend/utils/activity/
12.3% src/backend/utils/adt/
9.7% src/include/catalog/
4.0% src/include/utils/
9.2% src/test/regress/expected/
7.3% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index fa78031ccbb..ef89683164f 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..7cec0c83071 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,13 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static int pgStatBackendXactCommit = 0;
+static int pgStatBackendXactRollback = 0;
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +266,33 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += pgStatBackendXactCommit;
+ shbackendent->stats.xact_rollback += pgStatBackendXactRollback;
+
+ pgStatBackendXactCommit = pgStatBackendXactRollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +317,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +336,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +441,22 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ pgStatBackendXactCommit++;
+ else
+ pgStatBackendXactRollback++;
+
+ backend_has_xactstats = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..4c9f6b0dcec 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--ZnbSmJtA6EgjuGCe--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v2] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 166 insertions(+), 1 deletion(-)
26.1% doc/src/sgml/
29.1% src/backend/utils/activity/
11.9% src/backend/utils/adt/
9.4% src/include/catalog/
3.9% src/include/utils/
3.3% src/include/
8.9% src/test/regress/expected/
7.1% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 3f4a27a736e..77b41e02b82 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..12ae9a8d321 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,23 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..d3491faaff2 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Xact statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--SywurcztURg2uk+W--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v3 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. A new function is called in AtEOXact_PgStat() to increment those
two new counters.
---
src/backend/utils/activity/pgstat_backend.c | 57 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
4 files changed, 69 insertions(+), 1 deletion(-)
78.9% src/backend/utils/activity/
11.2% src/include/utils/
9.8% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..bf164854c4b 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,20 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /* Count transaction commit or abort */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..9efc0e10ebf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
--
2.34.1
--u6+Li7FU79Mt/lVd
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v3-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v4 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..47ce61e5093 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -36,7 +36,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index b31f20d41bc..400a8c5d734 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -342,6 +342,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..0fdbaf79780 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -801,6 +809,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..b97184c6539 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,7 +616,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--jg9kqX3osLGO+8A7
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v4-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v5 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 7727fed3bda..42bfb9cd38f 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -37,7 +37,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -48,6 +48,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -261,6 +266,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -285,6 +318,10 @@ pgstat_flush_backend(bool nowait, uint32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -300,6 +337,9 @@ pgstat_flush_backend(bool nowait, uint32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index 933dcb5cae5..218ff5e653c 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -352,6 +352,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 8e3549c3752..5d73c4dfbcf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -523,6 +523,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -535,6 +537,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -844,6 +852,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index eed4c6b359c..8077c65e938 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -707,7 +707,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, uint32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--uN5gGi2rtCNSVbpC
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v5-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v5 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 7727fed3bda..42bfb9cd38f 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -37,7 +37,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -48,6 +48,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -261,6 +266,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -285,6 +318,10 @@ pgstat_flush_backend(bool nowait, uint32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -300,6 +337,9 @@ pgstat_flush_backend(bool nowait, uint32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index 933dcb5cae5..218ff5e653c 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -352,6 +352,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 8e3549c3752..5d73c4dfbcf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -523,6 +523,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -535,6 +537,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -844,6 +852,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index eed4c6b359c..8077c65e938 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -707,7 +707,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, uint32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--uN5gGi2rtCNSVbpC
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v5-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v1] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 2 +
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 160 insertions(+), 1 deletion(-)
26.9% doc/src/sgml/
29.1% src/backend/utils/activity/
12.3% src/backend/utils/adt/
9.7% src/include/catalog/
4.0% src/include/utils/
9.2% src/test/regress/expected/
7.3% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index fa78031ccbb..ef89683164f 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..7cec0c83071 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,13 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static int pgStatBackendXactCommit = 0;
+static int pgStatBackendXactRollback = 0;
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +266,33 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += pgStatBackendXactCommit;
+ shbackendent->stats.xact_rollback += pgStatBackendXactRollback;
+
+ pgStatBackendXactCommit = pgStatBackendXactRollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +317,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +336,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +441,22 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ pgStatBackendXactCommit++;
+ else
+ pgStatBackendXactRollback++;
+
+ backend_has_xactstats = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..4c9f6b0dcec 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--ZnbSmJtA6EgjuGCe--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v2] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 166 insertions(+), 1 deletion(-)
26.1% doc/src/sgml/
29.1% src/backend/utils/activity/
11.9% src/backend/utils/adt/
9.4% src/include/catalog/
3.9% src/include/utils/
3.3% src/include/
8.9% src/test/regress/expected/
7.1% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 3f4a27a736e..77b41e02b82 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..12ae9a8d321 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,23 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..d3491faaff2 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Xact statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--SywurcztURg2uk+W--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v3 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. A new function is called in AtEOXact_PgStat() to increment those
two new counters.
---
src/backend/utils/activity/pgstat_backend.c | 57 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
4 files changed, 69 insertions(+), 1 deletion(-)
78.9% src/backend/utils/activity/
11.2% src/include/utils/
9.8% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..bf164854c4b 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,20 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /* Count transaction commit or abort */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..9efc0e10ebf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
--
2.34.1
--u6+Li7FU79Mt/lVd
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v3-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v4 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..47ce61e5093 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -36,7 +36,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index b31f20d41bc..400a8c5d734 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -342,6 +342,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..0fdbaf79780 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -801,6 +809,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..b97184c6539 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,7 +616,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--jg9kqX3osLGO+8A7
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v4-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v5 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 7727fed3bda..42bfb9cd38f 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -37,7 +37,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -48,6 +48,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -261,6 +266,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -285,6 +318,10 @@ pgstat_flush_backend(bool nowait, uint32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -300,6 +337,9 @@ pgstat_flush_backend(bool nowait, uint32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index 933dcb5cae5..218ff5e653c 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -352,6 +352,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 8e3549c3752..5d73c4dfbcf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -523,6 +523,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -535,6 +537,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -844,6 +852,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index eed4c6b359c..8077c65e938 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -707,7 +707,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, uint32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--uN5gGi2rtCNSVbpC
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v5-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v1] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 2 +
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 160 insertions(+), 1 deletion(-)
26.9% doc/src/sgml/
29.1% src/backend/utils/activity/
12.3% src/backend/utils/adt/
9.7% src/include/catalog/
4.0% src/include/utils/
9.2% src/test/regress/expected/
7.3% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index fa78031ccbb..ef89683164f 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..7cec0c83071 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,13 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static int pgStatBackendXactCommit = 0;
+static int pgStatBackendXactRollback = 0;
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +266,33 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += pgStatBackendXactCommit;
+ shbackendent->stats.xact_rollback += pgStatBackendXactRollback;
+
+ pgStatBackendXactCommit = pgStatBackendXactRollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +317,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +336,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +441,22 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ pgStatBackendXactCommit++;
+ else
+ pgStatBackendXactRollback++;
+
+ backend_has_xactstats = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..4c9f6b0dcec 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--ZnbSmJtA6EgjuGCe--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v2] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 166 insertions(+), 1 deletion(-)
26.1% doc/src/sgml/
29.1% src/backend/utils/activity/
11.9% src/backend/utils/adt/
9.4% src/include/catalog/
3.9% src/include/utils/
3.3% src/include/
8.9% src/test/regress/expected/
7.1% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 3f4a27a736e..77b41e02b82 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..12ae9a8d321 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,23 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..d3491faaff2 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Xact statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--SywurcztURg2uk+W--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v3 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. A new function is called in AtEOXact_PgStat() to increment those
two new counters.
---
src/backend/utils/activity/pgstat_backend.c | 57 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
4 files changed, 69 insertions(+), 1 deletion(-)
78.9% src/backend/utils/activity/
11.2% src/include/utils/
9.8% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..bf164854c4b 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,20 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /* Count transaction commit or abort */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..9efc0e10ebf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
--
2.34.1
--u6+Li7FU79Mt/lVd
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v3-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v4 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..47ce61e5093 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -36,7 +36,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index b31f20d41bc..400a8c5d734 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -342,6 +342,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..0fdbaf79780 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -801,6 +809,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..b97184c6539 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,7 +616,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--jg9kqX3osLGO+8A7
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v4-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v5 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 7727fed3bda..42bfb9cd38f 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -37,7 +37,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -48,6 +48,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -261,6 +266,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -285,6 +318,10 @@ pgstat_flush_backend(bool nowait, uint32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -300,6 +337,9 @@ pgstat_flush_backend(bool nowait, uint32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index 933dcb5cae5..218ff5e653c 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -352,6 +352,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 8e3549c3752..5d73c4dfbcf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -523,6 +523,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -535,6 +537,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -844,6 +852,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index eed4c6b359c..8077c65e938 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -707,7 +707,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, uint32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--uN5gGi2rtCNSVbpC
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v5-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v1] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 2 +
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 160 insertions(+), 1 deletion(-)
26.9% doc/src/sgml/
29.1% src/backend/utils/activity/
12.3% src/backend/utils/adt/
9.7% src/include/catalog/
4.0% src/include/utils/
9.2% src/test/regress/expected/
7.3% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index fa78031ccbb..ef89683164f 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..7cec0c83071 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,13 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static int pgStatBackendXactCommit = 0;
+static int pgStatBackendXactRollback = 0;
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +266,33 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += pgStatBackendXactCommit;
+ shbackendent->stats.xact_rollback += pgStatBackendXactRollback;
+
+ pgStatBackendXactCommit = pgStatBackendXactRollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +317,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +336,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +441,22 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ pgStatBackendXactCommit++;
+ else
+ pgStatBackendXactRollback++;
+
+ backend_has_xactstats = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..4c9f6b0dcec 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--ZnbSmJtA6EgjuGCe--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v2] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 166 insertions(+), 1 deletion(-)
26.1% doc/src/sgml/
29.1% src/backend/utils/activity/
11.9% src/backend/utils/adt/
9.4% src/include/catalog/
3.9% src/include/utils/
3.3% src/include/
8.9% src/test/regress/expected/
7.1% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 3f4a27a736e..77b41e02b82 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..12ae9a8d321 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,23 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..d3491faaff2 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Xact statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--SywurcztURg2uk+W--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v3 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. A new function is called in AtEOXact_PgStat() to increment those
two new counters.
---
src/backend/utils/activity/pgstat_backend.c | 57 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
4 files changed, 69 insertions(+), 1 deletion(-)
78.9% src/backend/utils/activity/
11.2% src/include/utils/
9.8% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..bf164854c4b 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,20 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /* Count transaction commit or abort */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..9efc0e10ebf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
--
2.34.1
--u6+Li7FU79Mt/lVd
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v3-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v4 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..47ce61e5093 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -36,7 +36,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index b31f20d41bc..400a8c5d734 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -342,6 +342,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..0fdbaf79780 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -801,6 +809,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..b97184c6539 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,7 +616,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--jg9kqX3osLGO+8A7
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v4-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v5 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 7727fed3bda..42bfb9cd38f 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -37,7 +37,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -48,6 +48,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -261,6 +266,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -285,6 +318,10 @@ pgstat_flush_backend(bool nowait, uint32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -300,6 +337,9 @@ pgstat_flush_backend(bool nowait, uint32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index 933dcb5cae5..218ff5e653c 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -352,6 +352,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 8e3549c3752..5d73c4dfbcf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -523,6 +523,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -535,6 +537,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -844,6 +852,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index eed4c6b359c..8077c65e938 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -707,7 +707,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, uint32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--uN5gGi2rtCNSVbpC
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v5-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v1] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 2 +
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 160 insertions(+), 1 deletion(-)
26.9% doc/src/sgml/
29.1% src/backend/utils/activity/
12.3% src/backend/utils/adt/
9.7% src/include/catalog/
4.0% src/include/utils/
9.2% src/test/regress/expected/
7.3% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index fa78031ccbb..ef89683164f 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..7cec0c83071 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,13 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static int pgStatBackendXactCommit = 0;
+static int pgStatBackendXactRollback = 0;
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +266,33 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += pgStatBackendXactCommit;
+ shbackendent->stats.xact_rollback += pgStatBackendXactRollback;
+
+ pgStatBackendXactCommit = pgStatBackendXactRollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +317,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +336,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +441,22 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ pgStatBackendXactCommit++;
+ else
+ pgStatBackendXactRollback++;
+
+ backend_has_xactstats = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..4c9f6b0dcec 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--ZnbSmJtA6EgjuGCe--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v2] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 166 insertions(+), 1 deletion(-)
26.1% doc/src/sgml/
29.1% src/backend/utils/activity/
11.9% src/backend/utils/adt/
9.4% src/include/catalog/
3.9% src/include/utils/
3.3% src/include/
8.9% src/test/regress/expected/
7.1% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 3f4a27a736e..77b41e02b82 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..12ae9a8d321 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,23 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..d3491faaff2 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Xact statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--SywurcztURg2uk+W--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v3 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. A new function is called in AtEOXact_PgStat() to increment those
two new counters.
---
src/backend/utils/activity/pgstat_backend.c | 57 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
4 files changed, 69 insertions(+), 1 deletion(-)
78.9% src/backend/utils/activity/
11.2% src/include/utils/
9.8% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..bf164854c4b 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,20 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /* Count transaction commit or abort */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..9efc0e10ebf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
--
2.34.1
--u6+Li7FU79Mt/lVd
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v3-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v4 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..47ce61e5093 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -36,7 +36,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index b31f20d41bc..400a8c5d734 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -342,6 +342,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..0fdbaf79780 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -801,6 +809,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..b97184c6539 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,7 +616,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--jg9kqX3osLGO+8A7
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v4-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v5 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 7727fed3bda..42bfb9cd38f 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -37,7 +37,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -48,6 +48,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -261,6 +266,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -285,6 +318,10 @@ pgstat_flush_backend(bool nowait, uint32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -300,6 +337,9 @@ pgstat_flush_backend(bool nowait, uint32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index 933dcb5cae5..218ff5e653c 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -352,6 +352,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 8e3549c3752..5d73c4dfbcf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -523,6 +523,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -535,6 +537,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -844,6 +852,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index eed4c6b359c..8077c65e938 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -707,7 +707,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, uint32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--uN5gGi2rtCNSVbpC
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v5-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v1] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 2 +
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 160 insertions(+), 1 deletion(-)
26.9% doc/src/sgml/
29.1% src/backend/utils/activity/
12.3% src/backend/utils/adt/
9.7% src/include/catalog/
4.0% src/include/utils/
9.2% src/test/regress/expected/
7.3% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index fa78031ccbb..ef89683164f 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..7cec0c83071 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,13 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static int pgStatBackendXactCommit = 0;
+static int pgStatBackendXactRollback = 0;
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +266,33 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += pgStatBackendXactCommit;
+ shbackendent->stats.xact_rollback += pgStatBackendXactRollback;
+
+ pgStatBackendXactCommit = pgStatBackendXactRollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +317,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +336,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +441,22 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ pgStatBackendXactCommit++;
+ else
+ pgStatBackendXactRollback++;
+
+ backend_has_xactstats = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..4c9f6b0dcec 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--ZnbSmJtA6EgjuGCe--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v2] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 166 insertions(+), 1 deletion(-)
26.1% doc/src/sgml/
29.1% src/backend/utils/activity/
11.9% src/backend/utils/adt/
9.4% src/include/catalog/
3.9% src/include/utils/
3.3% src/include/
8.9% src/test/regress/expected/
7.1% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 3f4a27a736e..77b41e02b82 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..12ae9a8d321 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,23 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..d3491faaff2 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Xact statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--SywurcztURg2uk+W--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v3 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. A new function is called in AtEOXact_PgStat() to increment those
two new counters.
---
src/backend/utils/activity/pgstat_backend.c | 57 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
4 files changed, 69 insertions(+), 1 deletion(-)
78.9% src/backend/utils/activity/
11.2% src/include/utils/
9.8% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..bf164854c4b 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,20 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /* Count transaction commit or abort */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..9efc0e10ebf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
--
2.34.1
--u6+Li7FU79Mt/lVd
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v3-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v4 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..47ce61e5093 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -36,7 +36,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index b31f20d41bc..400a8c5d734 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -342,6 +342,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..0fdbaf79780 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -801,6 +809,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..b97184c6539 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,7 +616,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--jg9kqX3osLGO+8A7
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v4-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v5 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 7727fed3bda..42bfb9cd38f 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -37,7 +37,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -48,6 +48,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -261,6 +266,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -285,6 +318,10 @@ pgstat_flush_backend(bool nowait, uint32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -300,6 +337,9 @@ pgstat_flush_backend(bool nowait, uint32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index 933dcb5cae5..218ff5e653c 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -352,6 +352,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 8e3549c3752..5d73c4dfbcf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -523,6 +523,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -535,6 +537,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -844,6 +852,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index eed4c6b359c..8077c65e938 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -707,7 +707,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, uint32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--uN5gGi2rtCNSVbpC
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v5-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v1] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 2 +
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 160 insertions(+), 1 deletion(-)
26.9% doc/src/sgml/
29.1% src/backend/utils/activity/
12.3% src/backend/utils/adt/
9.7% src/include/catalog/
4.0% src/include/utils/
9.2% src/test/regress/expected/
7.3% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index fa78031ccbb..ef89683164f 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..7cec0c83071 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,13 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static int pgStatBackendXactCommit = 0;
+static int pgStatBackendXactRollback = 0;
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +266,33 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += pgStatBackendXactCommit;
+ shbackendent->stats.xact_rollback += pgStatBackendXactRollback;
+
+ pgStatBackendXactCommit = pgStatBackendXactRollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +317,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +336,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +441,22 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ pgStatBackendXactCommit++;
+ else
+ pgStatBackendXactRollback++;
+
+ backend_has_xactstats = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..4c9f6b0dcec 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--ZnbSmJtA6EgjuGCe--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v2] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 166 insertions(+), 1 deletion(-)
26.1% doc/src/sgml/
29.1% src/backend/utils/activity/
11.9% src/backend/utils/adt/
9.4% src/include/catalog/
3.9% src/include/utils/
3.3% src/include/
8.9% src/test/regress/expected/
7.1% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 3f4a27a736e..77b41e02b82 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..12ae9a8d321 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,23 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..d3491faaff2 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Xact statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--SywurcztURg2uk+W--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v3 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. A new function is called in AtEOXact_PgStat() to increment those
two new counters.
---
src/backend/utils/activity/pgstat_backend.c | 57 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
4 files changed, 69 insertions(+), 1 deletion(-)
78.9% src/backend/utils/activity/
11.2% src/include/utils/
9.8% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..bf164854c4b 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,20 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /* Count transaction commit or abort */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..9efc0e10ebf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
--
2.34.1
--u6+Li7FU79Mt/lVd
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v3-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v4 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..47ce61e5093 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -36,7 +36,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index b31f20d41bc..400a8c5d734 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -342,6 +342,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..0fdbaf79780 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -801,6 +809,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..b97184c6539 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,7 +616,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--jg9kqX3osLGO+8A7
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v4-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v5 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 7727fed3bda..42bfb9cd38f 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -37,7 +37,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -48,6 +48,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -261,6 +266,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -285,6 +318,10 @@ pgstat_flush_backend(bool nowait, uint32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -300,6 +337,9 @@ pgstat_flush_backend(bool nowait, uint32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index 933dcb5cae5..218ff5e653c 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -352,6 +352,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 8e3549c3752..5d73c4dfbcf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -523,6 +523,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -535,6 +537,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -844,6 +852,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index eed4c6b359c..8077c65e938 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -707,7 +707,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, uint32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--uN5gGi2rtCNSVbpC
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v5-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v1] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 2 +
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 160 insertions(+), 1 deletion(-)
26.9% doc/src/sgml/
29.1% src/backend/utils/activity/
12.3% src/backend/utils/adt/
9.7% src/include/catalog/
4.0% src/include/utils/
9.2% src/test/regress/expected/
7.3% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index fa78031ccbb..ef89683164f 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..7cec0c83071 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,13 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static int pgStatBackendXactCommit = 0;
+static int pgStatBackendXactRollback = 0;
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +266,33 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += pgStatBackendXactCommit;
+ shbackendent->stats.xact_rollback += pgStatBackendXactRollback;
+
+ pgStatBackendXactCommit = pgStatBackendXactRollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +317,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +336,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +441,22 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ pgStatBackendXactCommit++;
+ else
+ pgStatBackendXactRollback++;
+
+ backend_has_xactstats = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..4c9f6b0dcec 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--ZnbSmJtA6EgjuGCe--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v2] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 166 insertions(+), 1 deletion(-)
26.1% doc/src/sgml/
29.1% src/backend/utils/activity/
11.9% src/backend/utils/adt/
9.4% src/include/catalog/
3.9% src/include/utils/
3.3% src/include/
8.9% src/test/regress/expected/
7.1% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 3f4a27a736e..77b41e02b82 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..12ae9a8d321 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,23 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..d3491faaff2 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Xact statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--SywurcztURg2uk+W--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v3 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. A new function is called in AtEOXact_PgStat() to increment those
two new counters.
---
src/backend/utils/activity/pgstat_backend.c | 57 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
4 files changed, 69 insertions(+), 1 deletion(-)
78.9% src/backend/utils/activity/
11.2% src/include/utils/
9.8% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..bf164854c4b 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,20 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /* Count transaction commit or abort */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..9efc0e10ebf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
--
2.34.1
--u6+Li7FU79Mt/lVd
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v3-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v4 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..47ce61e5093 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -36,7 +36,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index b31f20d41bc..400a8c5d734 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -342,6 +342,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..0fdbaf79780 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -801,6 +809,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..b97184c6539 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,7 +616,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--jg9kqX3osLGO+8A7
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v4-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v5 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 7727fed3bda..42bfb9cd38f 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -37,7 +37,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -48,6 +48,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -261,6 +266,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -285,6 +318,10 @@ pgstat_flush_backend(bool nowait, uint32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -300,6 +337,9 @@ pgstat_flush_backend(bool nowait, uint32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index 933dcb5cae5..218ff5e653c 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -352,6 +352,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 8e3549c3752..5d73c4dfbcf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -523,6 +523,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -535,6 +537,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -844,6 +852,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index eed4c6b359c..8077c65e938 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -707,7 +707,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, uint32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--uN5gGi2rtCNSVbpC
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v5-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v1] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 2 +
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 160 insertions(+), 1 deletion(-)
26.9% doc/src/sgml/
29.1% src/backend/utils/activity/
12.3% src/backend/utils/adt/
9.7% src/include/catalog/
4.0% src/include/utils/
9.2% src/test/regress/expected/
7.3% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index fa78031ccbb..ef89683164f 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..7cec0c83071 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,13 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static int pgStatBackendXactCommit = 0;
+static int pgStatBackendXactRollback = 0;
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +266,33 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += pgStatBackendXactCommit;
+ shbackendent->stats.xact_rollback += pgStatBackendXactRollback;
+
+ pgStatBackendXactCommit = pgStatBackendXactRollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +317,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +336,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +441,22 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ pgStatBackendXactCommit++;
+ else
+ pgStatBackendXactRollback++;
+
+ backend_has_xactstats = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..4c9f6b0dcec 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--ZnbSmJtA6EgjuGCe--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v2] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 166 insertions(+), 1 deletion(-)
26.1% doc/src/sgml/
29.1% src/backend/utils/activity/
11.9% src/backend/utils/adt/
9.4% src/include/catalog/
3.9% src/include/utils/
3.3% src/include/
8.9% src/test/regress/expected/
7.1% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 3f4a27a736e..77b41e02b82 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..12ae9a8d321 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,23 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..d3491faaff2 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Xact statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--SywurcztURg2uk+W--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v3 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. A new function is called in AtEOXact_PgStat() to increment those
two new counters.
---
src/backend/utils/activity/pgstat_backend.c | 57 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
4 files changed, 69 insertions(+), 1 deletion(-)
78.9% src/backend/utils/activity/
11.2% src/include/utils/
9.8% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..bf164854c4b 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,20 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /* Count transaction commit or abort */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..9efc0e10ebf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
--
2.34.1
--u6+Li7FU79Mt/lVd
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v3-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v4 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..47ce61e5093 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -36,7 +36,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index b31f20d41bc..400a8c5d734 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -342,6 +342,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..0fdbaf79780 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -801,6 +809,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..b97184c6539 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,7 +616,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--jg9kqX3osLGO+8A7
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v4-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v5 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 7727fed3bda..42bfb9cd38f 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -37,7 +37,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -48,6 +48,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -261,6 +266,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -285,6 +318,10 @@ pgstat_flush_backend(bool nowait, uint32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -300,6 +337,9 @@ pgstat_flush_backend(bool nowait, uint32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index 933dcb5cae5..218ff5e653c 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -352,6 +352,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 8e3549c3752..5d73c4dfbcf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -523,6 +523,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -535,6 +537,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -844,6 +852,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index eed4c6b359c..8077c65e938 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -707,7 +707,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, uint32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--uN5gGi2rtCNSVbpC
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v5-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v1] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 2 +
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 160 insertions(+), 1 deletion(-)
26.9% doc/src/sgml/
29.1% src/backend/utils/activity/
12.3% src/backend/utils/adt/
9.7% src/include/catalog/
4.0% src/include/utils/
9.2% src/test/regress/expected/
7.3% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index fa78031ccbb..ef89683164f 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..7cec0c83071 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,13 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static int pgStatBackendXactCommit = 0;
+static int pgStatBackendXactRollback = 0;
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +266,33 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += pgStatBackendXactCommit;
+ shbackendent->stats.xact_rollback += pgStatBackendXactRollback;
+
+ pgStatBackendXactCommit = pgStatBackendXactRollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +317,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +336,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +441,22 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ pgStatBackendXactCommit++;
+ else
+ pgStatBackendXactRollback++;
+
+ backend_has_xactstats = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..4c9f6b0dcec 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--ZnbSmJtA6EgjuGCe--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v2] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 166 insertions(+), 1 deletion(-)
26.1% doc/src/sgml/
29.1% src/backend/utils/activity/
11.9% src/backend/utils/adt/
9.4% src/include/catalog/
3.9% src/include/utils/
3.3% src/include/
8.9% src/test/regress/expected/
7.1% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 3f4a27a736e..77b41e02b82 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..12ae9a8d321 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,23 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..d3491faaff2 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Xact statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--SywurcztURg2uk+W--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v3 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. A new function is called in AtEOXact_PgStat() to increment those
two new counters.
---
src/backend/utils/activity/pgstat_backend.c | 57 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
4 files changed, 69 insertions(+), 1 deletion(-)
78.9% src/backend/utils/activity/
11.2% src/include/utils/
9.8% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..bf164854c4b 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,20 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /* Count transaction commit or abort */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..9efc0e10ebf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
--
2.34.1
--u6+Li7FU79Mt/lVd
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v3-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v4 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..47ce61e5093 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -36,7 +36,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index b31f20d41bc..400a8c5d734 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -342,6 +342,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..0fdbaf79780 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -801,6 +809,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..b97184c6539 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,7 +616,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--jg9kqX3osLGO+8A7
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v4-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v5 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 7727fed3bda..42bfb9cd38f 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -37,7 +37,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -48,6 +48,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -261,6 +266,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -285,6 +318,10 @@ pgstat_flush_backend(bool nowait, uint32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -300,6 +337,9 @@ pgstat_flush_backend(bool nowait, uint32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index 933dcb5cae5..218ff5e653c 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -352,6 +352,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 8e3549c3752..5d73c4dfbcf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -523,6 +523,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -535,6 +537,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -844,6 +852,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index eed4c6b359c..8077c65e938 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -707,7 +707,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, uint32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--uN5gGi2rtCNSVbpC
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v5-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v1] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 2 +
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 160 insertions(+), 1 deletion(-)
26.9% doc/src/sgml/
29.1% src/backend/utils/activity/
12.3% src/backend/utils/adt/
9.7% src/include/catalog/
4.0% src/include/utils/
9.2% src/test/regress/expected/
7.3% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index fa78031ccbb..ef89683164f 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..7cec0c83071 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,13 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static int pgStatBackendXactCommit = 0;
+static int pgStatBackendXactRollback = 0;
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +266,33 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += pgStatBackendXactCommit;
+ shbackendent->stats.xact_rollback += pgStatBackendXactRollback;
+
+ pgStatBackendXactCommit = pgStatBackendXactRollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +317,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +336,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +441,22 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ pgStatBackendXactCommit++;
+ else
+ pgStatBackendXactRollback++;
+
+ backend_has_xactstats = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..4c9f6b0dcec 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--ZnbSmJtA6EgjuGCe--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v2] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 166 insertions(+), 1 deletion(-)
26.1% doc/src/sgml/
29.1% src/backend/utils/activity/
11.9% src/backend/utils/adt/
9.4% src/include/catalog/
3.9% src/include/utils/
3.3% src/include/
8.9% src/test/regress/expected/
7.1% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 3f4a27a736e..77b41e02b82 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..12ae9a8d321 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,23 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..d3491faaff2 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Xact statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--SywurcztURg2uk+W--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v3 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. A new function is called in AtEOXact_PgStat() to increment those
two new counters.
---
src/backend/utils/activity/pgstat_backend.c | 57 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
4 files changed, 69 insertions(+), 1 deletion(-)
78.9% src/backend/utils/activity/
11.2% src/include/utils/
9.8% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..bf164854c4b 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,20 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /* Count transaction commit or abort */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..9efc0e10ebf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
--
2.34.1
--u6+Li7FU79Mt/lVd
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v3-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v4 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..47ce61e5093 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -36,7 +36,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index b31f20d41bc..400a8c5d734 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -342,6 +342,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..0fdbaf79780 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -801,6 +809,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..b97184c6539 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,7 +616,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--jg9kqX3osLGO+8A7
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v4-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v5 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 7727fed3bda..42bfb9cd38f 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -37,7 +37,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -48,6 +48,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -261,6 +266,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -285,6 +318,10 @@ pgstat_flush_backend(bool nowait, uint32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -300,6 +337,9 @@ pgstat_flush_backend(bool nowait, uint32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index 933dcb5cae5..218ff5e653c 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -352,6 +352,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 8e3549c3752..5d73c4dfbcf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -523,6 +523,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -535,6 +537,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -844,6 +852,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index eed4c6b359c..8077c65e938 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -707,7 +707,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, uint32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--uN5gGi2rtCNSVbpC
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v5-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v1] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 2 +
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 160 insertions(+), 1 deletion(-)
26.9% doc/src/sgml/
29.1% src/backend/utils/activity/
12.3% src/backend/utils/adt/
9.7% src/include/catalog/
4.0% src/include/utils/
9.2% src/test/regress/expected/
7.3% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index fa78031ccbb..ef89683164f 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..7cec0c83071 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,13 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static int pgStatBackendXactCommit = 0;
+static int pgStatBackendXactRollback = 0;
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +266,33 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += pgStatBackendXactCommit;
+ shbackendent->stats.xact_rollback += pgStatBackendXactRollback;
+
+ pgStatBackendXactCommit = pgStatBackendXactRollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +317,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +336,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +441,22 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ pgStatBackendXactCommit++;
+ else
+ pgStatBackendXactRollback++;
+
+ backend_has_xactstats = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..4c9f6b0dcec 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--ZnbSmJtA6EgjuGCe--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v2] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 166 insertions(+), 1 deletion(-)
26.1% doc/src/sgml/
29.1% src/backend/utils/activity/
11.9% src/backend/utils/adt/
9.4% src/include/catalog/
3.9% src/include/utils/
3.3% src/include/
8.9% src/test/regress/expected/
7.1% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 3f4a27a736e..77b41e02b82 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..12ae9a8d321 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,23 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..d3491faaff2 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Xact statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--SywurcztURg2uk+W--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v3 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. A new function is called in AtEOXact_PgStat() to increment those
two new counters.
---
src/backend/utils/activity/pgstat_backend.c | 57 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
4 files changed, 69 insertions(+), 1 deletion(-)
78.9% src/backend/utils/activity/
11.2% src/include/utils/
9.8% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..bf164854c4b 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,20 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /* Count transaction commit or abort */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..9efc0e10ebf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
--
2.34.1
--u6+Li7FU79Mt/lVd
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v3-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v1] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 2 +
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 160 insertions(+), 1 deletion(-)
26.9% doc/src/sgml/
29.1% src/backend/utils/activity/
12.3% src/backend/utils/adt/
9.7% src/include/catalog/
4.0% src/include/utils/
9.2% src/test/regress/expected/
7.3% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index fa78031ccbb..ef89683164f 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..7cec0c83071 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,13 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static int pgStatBackendXactCommit = 0;
+static int pgStatBackendXactRollback = 0;
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +266,33 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += pgStatBackendXactCommit;
+ shbackendent->stats.xact_rollback += pgStatBackendXactRollback;
+
+ pgStatBackendXactCommit = pgStatBackendXactRollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +317,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +336,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +441,22 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ pgStatBackendXactCommit++;
+ else
+ pgStatBackendXactRollback++;
+
+ backend_has_xactstats = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..4c9f6b0dcec 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--ZnbSmJtA6EgjuGCe--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v2] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 166 insertions(+), 1 deletion(-)
26.1% doc/src/sgml/
29.1% src/backend/utils/activity/
11.9% src/backend/utils/adt/
9.4% src/include/catalog/
3.9% src/include/utils/
3.3% src/include/
8.9% src/test/regress/expected/
7.1% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 3f4a27a736e..77b41e02b82 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..12ae9a8d321 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,23 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..d3491faaff2 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Xact statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--SywurcztURg2uk+W--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v3 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. A new function is called in AtEOXact_PgStat() to increment those
two new counters.
---
src/backend/utils/activity/pgstat_backend.c | 57 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
4 files changed, 69 insertions(+), 1 deletion(-)
78.9% src/backend/utils/activity/
11.2% src/include/utils/
9.8% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..bf164854c4b 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,20 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /* Count transaction commit or abort */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..9efc0e10ebf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
--
2.34.1
--u6+Li7FU79Mt/lVd
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v3-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v1] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 2 +
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 160 insertions(+), 1 deletion(-)
26.9% doc/src/sgml/
29.1% src/backend/utils/activity/
12.3% src/backend/utils/adt/
9.7% src/include/catalog/
4.0% src/include/utils/
9.2% src/test/regress/expected/
7.3% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index fa78031ccbb..ef89683164f 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..7cec0c83071 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,13 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static int pgStatBackendXactCommit = 0;
+static int pgStatBackendXactRollback = 0;
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +266,33 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += pgStatBackendXactCommit;
+ shbackendent->stats.xact_rollback += pgStatBackendXactRollback;
+
+ pgStatBackendXactCommit = pgStatBackendXactRollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +317,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +336,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +441,22 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ pgStatBackendXactCommit++;
+ else
+ pgStatBackendXactRollback++;
+
+ backend_has_xactstats = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..4c9f6b0dcec 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--ZnbSmJtA6EgjuGCe--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v2] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 166 insertions(+), 1 deletion(-)
26.1% doc/src/sgml/
29.1% src/backend/utils/activity/
11.9% src/backend/utils/adt/
9.4% src/include/catalog/
3.9% src/include/utils/
3.3% src/include/
8.9% src/test/regress/expected/
7.1% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 3f4a27a736e..77b41e02b82 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..12ae9a8d321 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,23 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..d3491faaff2 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Xact statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--SywurcztURg2uk+W--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v3 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. A new function is called in AtEOXact_PgStat() to increment those
two new counters.
---
src/backend/utils/activity/pgstat_backend.c | 57 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
4 files changed, 69 insertions(+), 1 deletion(-)
78.9% src/backend/utils/activity/
11.2% src/include/utils/
9.8% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..bf164854c4b 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,20 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /* Count transaction commit or abort */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..9efc0e10ebf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
--
2.34.1
--u6+Li7FU79Mt/lVd
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v3-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v1] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 2 +
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 160 insertions(+), 1 deletion(-)
26.9% doc/src/sgml/
29.1% src/backend/utils/activity/
12.3% src/backend/utils/adt/
9.7% src/include/catalog/
4.0% src/include/utils/
9.2% src/test/regress/expected/
7.3% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index fa78031ccbb..ef89683164f 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..7cec0c83071 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,13 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static int pgStatBackendXactCommit = 0;
+static int pgStatBackendXactRollback = 0;
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +266,33 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += pgStatBackendXactCommit;
+ shbackendent->stats.xact_rollback += pgStatBackendXactRollback;
+
+ pgStatBackendXactCommit = pgStatBackendXactRollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +317,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +336,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +441,22 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ pgStatBackendXactCommit++;
+ else
+ pgStatBackendXactRollback++;
+
+ backend_has_xactstats = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..4c9f6b0dcec 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--ZnbSmJtA6EgjuGCe--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v2] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 166 insertions(+), 1 deletion(-)
26.1% doc/src/sgml/
29.1% src/backend/utils/activity/
11.9% src/backend/utils/adt/
9.4% src/include/catalog/
3.9% src/include/utils/
3.3% src/include/
8.9% src/test/regress/expected/
7.1% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 3f4a27a736e..77b41e02b82 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..12ae9a8d321 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,23 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..d3491faaff2 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Xact statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--SywurcztURg2uk+W--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v3 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. A new function is called in AtEOXact_PgStat() to increment those
two new counters.
---
src/backend/utils/activity/pgstat_backend.c | 57 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
4 files changed, 69 insertions(+), 1 deletion(-)
78.9% src/backend/utils/activity/
11.2% src/include/utils/
9.8% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..bf164854c4b 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,20 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /* Count transaction commit or abort */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..9efc0e10ebf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
--
2.34.1
--u6+Li7FU79Mt/lVd
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v3-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v4 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
src/backend/utils/activity/pgstat_backend.c | 42 +++++++++++++++++++-
src/backend/utils/activity/pgstat_database.c | 7 ++++
src/include/pgstat.h | 15 +++++++
src/include/utils/pgstat_internal.h | 3 +-
4 files changed, 65 insertions(+), 2 deletions(-)
74.4% src/backend/utils/activity/
7.8% src/include/utils/
17.7% src/include/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..47ce61e5093 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -36,7 +36,7 @@
* reported within critical sections so we use static memory in order to avoid
* memory allocation.
*/
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
static bool backend_has_iostats = false;
/*
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend transaction statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * transaction statistics. In this case, avoid unnecessarily modifying
+ * the stats entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some transaction data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index b31f20d41bc..400a8c5d734 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -342,6 +342,13 @@ pgstat_update_dbstats(TimestampTz ts)
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
+ /* Do the same for backend stats */
+ PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+ PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+
if (pgstat_should_report_connstat())
{
long secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..0fdbaf79780 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Transaction statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
@@ -801,6 +809,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
extern PGDLLIMPORT int pgstat_fetch_consistency;
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
/*
* Variables in pgstat_bgwriter.c
*/
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..b97184c6539 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,7 +616,8 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
--
2.34.1
--jg9kqX3osLGO+8A7
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v4-0002-Adding-XID-generation-count-per-backend.patch"
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v2] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 8 +++
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 166 insertions(+), 1 deletion(-)
26.1% doc/src/sgml/
29.1% src/backend/utils/activity/
11.9% src/backend/utils/adt/
9.4% src/include/catalog/
3.9% src/include/utils/
3.3% src/include/
8.9% src/test/regress/expected/
7.1% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 3f4a27a736e..77b41e02b82 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..12ae9a8d321 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+ shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+ PendingBackendStats.pending_xact_commit = 0;
+ PendingBackendStats.pending_xact_rollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +440,23 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ PendingBackendStats.pending_xact_commit++;
+ else
+ PendingBackendStats.pending_xact_rollback++;
+
+ backend_has_xactstats = true;
+ pgstat_report_fixed = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..d3491faaff2 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
* Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
*/
PgStat_PendingIO pending_io;
+
+ /*
+ * Xact statistics pending flush.
+ */
+ PgStat_Counter pending_xact_commit;
+ PgStat_Counter pending_xact_rollback;
} PgStat_BackendPending;
/*
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--SywurcztURg2uk+W--
^ permalink raw reply [nested|flat] 953+ messages in thread
* [PATCH v1] Adding per backend commit and rollback counters
@ 2025-08-04 08:14 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)
This commit adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
It relies on the existing per backend statistics that has been added in
9aea73fc61d.
---
doc/src/sgml/monitoring.sgml | 36 +++++++++++++
src/backend/utils/activity/pgstat_backend.c | 60 +++++++++++++++++++++
src/backend/utils/activity/pgstat_xact.c | 1 +
src/backend/utils/adt/pgstatfuncs.c | 22 ++++++++
src/include/catalog/pg_proc.dat | 9 ++++
src/include/pgstat.h | 2 +
src/include/utils/pgstat_internal.h | 4 +-
src/test/regress/expected/stats.out | 17 ++++++
src/test/regress/sql/stats.sql | 10 ++++
9 files changed, 160 insertions(+), 1 deletion(-)
26.9% doc/src/sgml/
29.1% src/backend/utils/activity/
12.3% src/backend/utils/adt/
9.7% src/include/catalog/
4.0% src/include/utils/
9.2% src/test/regress/expected/
7.3% src/test/regress/sql/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index fa78031ccbb..ef89683164f 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4921,6 +4921,42 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry id="pg-stat-get-backend-xact-commit" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_commit</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_commit</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been committed by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry id="pg-stat-get-backend-xact-rollback" role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_stat_get_backend_xact_rollback</primary>
+ </indexterm>
+ <function>pg_stat_get_backend_xact_rollback</function> ( <type>integer</type> )
+ <returnvalue>bigint</returnvalue>
+ </para>
+ <para>
+ Returns the number of transactions that have been rolled back by the backend
+ with the specified process ID.
+ </para>
+ <para>
+ The function does not return statistics for the checkpointer,
+ the background writer, the startup process and the autovacuum launcher.
+ </para></entry>
+ </row>
+
<row>
<entry id="pg-stat-get-backend-wal" role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..7cec0c83071 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -47,6 +47,13 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * For backend commit and rollback statistics.
+ */
+static int pgStatBackendXactCommit = 0;
+static int pgStatBackendXactRollback = 0;
+static bool backend_has_xactstats = false;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -259,6 +266,33 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend xact statistics. Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+
+ /*
+ * This function can be called even if nothing at all has happened for
+ * XACT statistics. In this case, avoid unnecessarily modifying the stats
+ * entry.
+ */
+ if (!backend_has_xactstats)
+ return;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+ shbackendent->stats.xact_commit += pgStatBackendXactCommit;
+ shbackendent->stats.xact_rollback += pgStatBackendXactRollback;
+
+ pgStatBackendXactCommit = pgStatBackendXactRollback = 0;
+
+ backend_has_xactstats = false;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -283,6 +317,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some XACT data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -298,6 +336,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+ pgstat_flush_backend_entry_xact(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -400,3 +441,22 @@ pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
}
+
+void
+AtEOXact_PgStat_Backend(bool isCommit, bool parallel)
+{
+ /* Don't count parallel worker transaction stats */
+ if (!parallel)
+ {
+ /*
+ * Count transaction commit or abort. (We use counters, not just
+ * bools, in case the reporting message isn't sent right away.)
+ */
+ if (isCommit)
+ pgStatBackendXactCommit++;
+ else
+ pgStatBackendXactRollback++;
+
+ backend_has_xactstats = true;
+ }
+}
diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index bc9864bd8d9..cd1c501c165 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -42,6 +42,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
PgStat_SubXactStatus *xact_state;
AtEOXact_PgStat_Database(isCommit, parallel);
+ AtEOXact_PgStat_Backend(isCommit, parallel);
/* handle transactional stats information */
xact_state = pgStatXactStack;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..6436129f516 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,28 @@ pg_stat_get_backend_io(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+#define PG_STAT_GET_BACKENDENTRY_INT64(stat) \
+Datum \
+CppConcat(pg_stat_get_backend_,stat)(PG_FUNCTION_ARGS) \
+{ \
+ int pid; \
+ PgStat_Backend *backend_stats; \
+ \
+ pid = PG_GETARG_INT32(0); \
+ backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL);\
+ \
+ if (!backend_stats) \
+ PG_RETURN_NULL(); \
+ else \
+ PG_RETURN_INT64(backend_stats->stat); \
+}
+
+/* pg_stat_get_backend_xact_commit */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_commit)
+
+/* pg_stat_get_backend_xact_rollback */
+PG_STAT_GET_BACKENDENTRY_INT64(xact_rollback)
+
/*
* pg_stat_wal_build_tuple
*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace..f5085d4e016 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6010,6 +6010,15 @@
proargnames => '{backend_pid,backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_backend_io' },
+{ oid => '8170', descr => 'statistics: backend transactions committed',
+ proname => 'pg_stat_get_backend_xact_commit', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_commit' },
+{ oid => '8916', descr => 'statistics: backend transactions rolled back',
+ proname => 'pg_stat_get_backend_xact_rollback', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'int4',
+ prosrc => 'pg_stat_get_backend_xact_rollback' },
+
{ oid => '1136', descr => 'statistics: information about WAL activity',
proname => 'pg_stat_get_wal', proisstrict => 'f', provolatile => 's',
proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..4c9f6b0dcec 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_Counter xact_commit;
+ PgStat_Counter xact_rollback;
} PgStat_Backend;
/* ---------
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..23ea8ffd618 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,12 +616,14 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT (1 << 2) /* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
TimestampTz ts);
+extern void AtEOXact_PgStat_Backend(bool isCommit, bool parallel);
/*
* Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..0d316f94e40 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -135,11 +135,28 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+SELECT :xact_commit_after > :xact_commit_before;
+ ?column?
+----------
+ t
+(1 row)
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..d629140d880 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -58,12 +58,22 @@ INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
+-- in passing, check that backend's commit is incrementing
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_before
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
+SELECT pg_stat_force_next_flush();
+SELECT pg_stat_get_backend_xact_commit AS xact_commit_after
+ FROM pg_stat_get_backend_xact_commit(pg_backend_pid()) \gset
+
+SELECT :xact_commit_after > :xact_commit_before;
+
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
--
2.34.1
--ZnbSmJtA6EgjuGCe--
^ permalink raw reply [nested|flat] 953+ messages in thread
* Adding per backend commit and rollback counters
@ 2025-08-04 14:20 Bertrand Drouvot <[email protected]>
0 siblings, 1 reply; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-04 14:20 UTC (permalink / raw)
To: PostgreSQL Hackers <[email protected]>
Hi hackers,
PFA a patch for $SUBJECT.
Currently we can find xact_commit and xact_rollback in pg_stat_database but we
don't have this information per backend.
This patch adds 2 functions: pg_stat_get_backend_xact_commit() and
pg_stat_get_backend_xact_rollback() to report the number of transactions that
have been committed/rolled back for a given backend PID.
I think having this information per-backend could be useful, for example, to:
- check which application is producing the highest number of commit / rollback
- check if the application's hosts have "uniform" commit/rollback pattern
- check if some application's hosts are doing a lot of rollback (as compared
to the other hosts): that could mean those hosts are not using an up-to-date
application version
This patch is pretty straightforward as it relies on the existing per backend
statistics machinery that has been added in 9aea73fc61d (so that there is not
that much design to discuss).
On a side note, I noticed that when a transaction fails, say this way:
postgres=# insert into bdt2 values(1);
ERROR: relation "bdt2" does not exist
Then the existing pg_stat_get_db_xact_rollback() does not return the rollback
increment (so does pg_stat_database.xact_rollback). Indeed, the flush is done
during the next commit or explicit rollback.
Maybe we could add an extra counter, that tracks the transactions that have not
been explicitly rolled back (xact_error or such) and flush it at the right time.
Looking forward to your feedback,
Regards,
--
Bertrand Drouvot
PostgreSQL Contributors Team
RDS Open Source Databases
Amazon Web Services: https://aws.amazon.com
^ permalink raw reply [nested|flat] 953+ messages in thread
* Re: Adding per backend commit and rollback counters
@ 2025-08-07 08:17 Bertrand Drouvot <[email protected]>
parent: Bertrand Drouvot <[email protected]>
0 siblings, 1 reply; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-07 08:17 UTC (permalink / raw)
To: PostgreSQL Hackers <[email protected]>
Hi,
On Mon, Aug 04, 2025 at 02:20:48PM +0000, Bertrand Drouvot wrote:
> This patch is pretty straightforward as it relies on the existing per backend
> statistics machinery that has been added in 9aea73fc61d (so that there is not
> that much design to discuss).
Still, while working on adding more backend stats (more on that later), I
realized that in v1, I missed to use pgstat_report_fixed (recently added in
793928c2d5a): the attached fixes that.
Also, I think it's better to put the new xact pending counters in the existing
PgStat_BackendPending, done that way in the attached.
Regards,
--
Bertrand Drouvot
PostgreSQL Contributors Team
RDS Open Source Databases
Amazon Web Services: https://aws.amazon.com
^ permalink raw reply [nested|flat] 953+ messages in thread
* Re: Adding per backend commit and rollback counters
@ 2025-08-10 07:47 Bertrand Drouvot <[email protected]>
parent: Bertrand Drouvot <[email protected]>
0 siblings, 1 reply; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-10 07:47 UTC (permalink / raw)
To: PostgreSQL Hackers <[email protected]>
Hi,
On Thu, Aug 07, 2025 at 08:17:26AM +0000, Bertrand Drouvot wrote:
> Hi,
>
> On Mon, Aug 04, 2025 at 02:20:48PM +0000, Bertrand Drouvot wrote:
> > This patch is pretty straightforward as it relies on the existing per backend
> > statistics machinery that has been added in 9aea73fc61d (so that there is not
> > that much design to discuss).
>
> Still, while working on adding more backend stats (more on that later), I
> realized that in v1, I missed to use pgstat_report_fixed (recently added in
> 793928c2d5a): the attached fixes that.
>
> Also, I think it's better to put the new xact pending counters in the existing
> PgStat_BackendPending, done that way in the attached.
Another metric that could be useful is to track the XIDs generated by backend.
That would help to see if a backend is consuming XIDs at a high rate.
We can not rely on the number of commits or rollbacks as they take into account
the virtual transactions.
So a new counter has been added in 0002 attached. Also, v3 changes the way
the statistics are displayed. I've in mind to add much more statistics per backend
(such a number of seqscans, vacuum count, analyze count..., I'll open a dedicated
thread for those) and I think that a single view to display them all makes
more sense than a lot of individual functions. This view is added in 0003.
To sum up, v3 contains:
0001 -
Adding per backend commit and rollback counters
It relies on the existing per backend statistics that has been added in
9aea73fc61d. A new function is called in AtEOXact_PgStat() to increment those
two new counters.
0002 -
Adding XID generation count per backend
This patch adds a new counter to record the number of XIDs generated per
backend. It will help to detect if a backend is consuming XIDs at a high rate.
Virtual transactions are not taken into account on purpose, we do want to track
only the XID where there is a risk of wraparound.
The counter is not part of PgStat_BackendPending, because we want to avoid
an extra function call in this code path to increment the counter in
PendingBackendStats. The counter increment here behaves more or less the same as
we do for WAL statistics.
0003 -
Adding the pg_stat_backend view
This view displays one row per server process, showing statistics related to
the current activity of that process. It currently displays the pid, the
number of XIDs generated, the number of commits, the number of rollbacks and the
time at which these statistics were last reset.
It's built on top of a new function (pg_stat_get_backend_statistics()). The idea
is the same as pg_stat_activity and pg_stat_get_activity().
Adding documentation and tests.
Regards,
--
Bertrand Drouvot
PostgreSQL Contributors Team
RDS Open Source Databases
Amazon Web Services: https://aws.amazon.com
^ permalink raw reply [nested|flat] 953+ messages in thread
* Re: Adding per backend commit and rollback counters
@ 2025-08-28 16:07 Bertrand Drouvot <[email protected]>
parent: Bertrand Drouvot <[email protected]>
0 siblings, 1 reply; 953+ messages in thread
From: Bertrand Drouvot @ 2025-08-28 16:07 UTC (permalink / raw)
To: PostgreSQL Hackers <[email protected]>
Hi,
On Sun, Aug 10, 2025 at 07:47:09AM +0000, Bertrand Drouvot wrote:
> To sum up, v3 contains:
>
> 0001 -
> Adding per backend commit and rollback counters
> 0002 -
> Adding XID generation count per backend
> 0003 -
> Adding the pg_stat_backend view
Following recent conversations in [1], those changes have been made in v4
attached:
- avoid tracking the commit and rollback counters twice (for databases and for
backends) but increment the backend stats when the database ones are flushed. Same
idea as [2].
- pg_stat_backend is too generic (see [3]), let's use pg_stat_backend_transaction
instead. I deliberately did not use pg_stat_backend_xact to not confuse with the
other "*xact*" functions/views where the meaning is not the same. I'm open to
other naming suggestion though.
[1]: https://www.postgresql.org/message-id/flat/aJrxug4LCg4Hm5Mm%40ip-10-97-1-34.eu-west-3.compute.intern...
[2]: https://www.postgresql.org/message-id/7fhpds4xqk6bnudzmzkqi33pinsxammpljwde5gfkjdygvejrj%40ojkzfr7dx...
[3]: https://www.postgresql.org/message-id/aK8OuVPmmDTc9CFX%40ip-10-97-1-34.eu-west-3.compute.internal
Regards,
--
Bertrand Drouvot
PostgreSQL Contributors Team
RDS Open Source Databases
Amazon Web Services: https://aws.amazon.com
^ permalink raw reply [nested|flat] 953+ messages in thread
* Re: Adding per backend commit and rollback counters
@ 2026-03-30 20:19 Kuba Knysiak <[email protected]>
parent: Bertrand Drouvot <[email protected]>
0 siblings, 1 reply; 953+ messages in thread
From: Kuba Knysiak @ 2026-03-30 20:19 UTC (permalink / raw)
To: [email protected]; +Cc: Bertrand Drouvot <[email protected]>
Hello,
after reviewing the patch together with Miłosz, we found the following:
- In pgstatfuncs.c, we call pgstat_fetch_stat_backend_by_pid(beentry->st_procpid, NULL) for each backend row. That path acquires ProcArrayLock via BackendPidGetProc(), so this repeats lock acquisition for every row. We could simplify this and avoid taking the lock altogether by fetching directly with pgstat_fetch_stat_backend(local_beentry->proc_number).
Also, shouldn't this patch bump catversion?
Regards,
Kuba
The new status of this patch is: Waiting on Author
^ permalink raw reply [nested|flat] 953+ messages in thread
* Re: Adding per backend commit and rollback counters
@ 2026-03-31 09:24 Bertrand Drouvot <[email protected]>
parent: Kuba Knysiak <[email protected]>
0 siblings, 1 reply; 953+ messages in thread
From: Bertrand Drouvot @ 2026-03-31 09:24 UTC (permalink / raw)
To: Kuba Knysiak <[email protected]>; +Cc: [email protected]
Hi,
On Mon, Mar 30, 2026 at 08:19:13PM +0000, Kuba Knysiak wrote:
> Hello,
> after reviewing the patch together with Miłosz, we found the following:
Thanks for the review!
> - In pgstatfuncs.c, we call pgstat_fetch_stat_backend_by_pid(beentry->st_procpid, NULL)
> for each backend row. That path acquires ProcArrayLock via BackendPidGetProc(),
> so this repeats lock acquisition for every row. We could simplify this and avoid
> taking the lock altogether by fetching directly with
> pgstat_fetch_stat_backend(local_beentry->proc_number).
Yeah, I think that's a good point. Done that way in the attached. Also adding a
check on the backend type as it was done in pgstat_fetch_stat_backend_by_pid().
> Also, shouldn't this patch bump catversion?
Yes and that was mentioned in the 0003 commit message. We usually don't change
it in the patch itself (could easily produce rebase noise) but just put an
XXX in the commit message so that it's not forgoten when pushed.
Regards,
--
Bertrand Drouvot
PostgreSQL Contributors Team
RDS Open Source Databases
Amazon Web Services: https://aws.amazon.com
^ permalink raw reply [nested|flat] 953+ messages in thread
* Re: Adding per backend commit and rollback counters
@ 2026-07-06 11:41 Nishant Sharma <[email protected]>
parent: Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 953+ messages in thread
From: Nishant Sharma @ 2026-07-06 11:41 UTC (permalink / raw)
To: Bertrand Drouvot <[email protected]>; +Cc: Kuba Knysiak <[email protected]>; [email protected]
Thanks for the v5 patch set!
Here are my review comments:
1) Patches need re-base. Not able to apply.
2) PGSTAT_BACKEND_FLUSH_XACT collision with newly committed
PGSTAT_BACKEND_FLUSH_LOCK.
3) What if pgStatXactCommit and pgStatXactRollback are 0? We unnecessarily
make backend_has_xactstats as true while updating the pending struct
values. In this process unnecessary ref_lock would be taken and
stats.xact_commit, stats.xact_rollback would be updated with 0 and then
ref_lock is unlocked.
4) PendingBackendStats was static but it was removed in this change, yet
the comment above still mentions it. Also it is killing the purpose of
"Utility routines to report I/O stats for backends ..." comment mentioned
in pgstat_backend.c. It appears the existing helper I/O routines' purpose
is eliminated. This doesn't look good.
4) I don't see any function using plural form like
pg_stat_get_backend_transactions in pg_proc.dat
5) The thread started with only commit and rollback counters, but xid was
added in v3, along with the idea of adding more, such as "seqscans, vacuum
count, analyze count...". This makes me curious: why can't these other
counters be added in this same thread? If these others aren't added now,
why was xid added? I suggest this thread should only include commit and
rollback counters, and xid should be added with the other metrics in a new
thread.
Regards,
Nishant Sharma,
EDB, Pune.
https://www.enterprisedb.com/
^ permalink raw reply [nested|flat] 953+ messages in thread
end of thread, other threads:[~2026-07-06 11:41 UTC | newest]
Thread overview: 953+ messages (download: mbox mbox.gz follow: Atom feed)
-- links below jump to the message on this page --
2023-05-31 10:08 [PATCH v31 07/11] Add DISTINCT support for IVM Yugo Nagata <[email protected]>
2025-08-04 08:14 [PATCH v2] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v2] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v4 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v4 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v1] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v2] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v5 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v2] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v5 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v1] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v2] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v5 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v3 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v1] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v4 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v1] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v4 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v4 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v5 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v2] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v2] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v4 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v1] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v5 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v3 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v3 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v4 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v2] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v5 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v1] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v5 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v4 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v1] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v3 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v1] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v5 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v3 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v3 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v3 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v4 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v4 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v5 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v5 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v3 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v2] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v1] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v5 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v1] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v1] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v2] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v2] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v2] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v3 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v4 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v1] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v2] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v4 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v3 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v1] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v4 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v4 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v2] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v3 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v3 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v4 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v3 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v5 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v3 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v2] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v5 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v5 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v5 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v4 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v5 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v1] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v5 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v5 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v5 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v1] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v1] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v5 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v2] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v5 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v4 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v4 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v1] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v2] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v4 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v4 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v2] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v3 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v2] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v2] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v5 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v4 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v2] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v2] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v2] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v3 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v4 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v1] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v1] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v4 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v1] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v5 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v5 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v3 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v5 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v2] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v2] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v4 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v3 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v2] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v4 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v1] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v2] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v3 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v1] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v5 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v2] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v2] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v5 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v4 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v2] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v4 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v5 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v3 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v3 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v4 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v2] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v5 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v5 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v5 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v3 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v2] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v2] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v1] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v2] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v1] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v2] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v4 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v2] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v1] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v4 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v4 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v5 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v4 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v4 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v1] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v4 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v2] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v1] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v5 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v5 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v4 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v1] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v1] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v5 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v2] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v3 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v4 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v3 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v3 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v5 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v4 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v4 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v5 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v1] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v3 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v5 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v3 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v2] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v2] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v5 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v3 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v2] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v1] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v5 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v5 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v3 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v3 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v5 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v3 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v1] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v5 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v3 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v5 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v4 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v2] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v2] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v4 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v3 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v3 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v2] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v1] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v4 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v2] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v1] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v2] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v1] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v1] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v2] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v4 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v5 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v1] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v4 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v2] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v4 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v3 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v1] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v5 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v2] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v4 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v5 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v5 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v2] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v1] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v1] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v5 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v5 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v1] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v5 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v5 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v3 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v2] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v3 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v1] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v3 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v1] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v4 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v4 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v4 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v2] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v2] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v2] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v1] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v3 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v1] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v5 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v5 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v5 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v3 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v1] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v5 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v3 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v2] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v3 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v3 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v4 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v4 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v1] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v4 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v1] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v4 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v2] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v4 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v3 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v4 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v2] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v2] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v1] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v2] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v5 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v5 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v4 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v3 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v1] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v3 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v2] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v1] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v1] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v2] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v4 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v5 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v1] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v5 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v4 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v4 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v1] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v1] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v4 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v1] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v5 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v1] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v5 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v2] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v4 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v4 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v2] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v3 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v4 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v2] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v1] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v4 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v4 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v3 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v1] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v2] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v2] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v5 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v5 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v3 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v2] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v3 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v2] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v3 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v1] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v4 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v1] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v4 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v4 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v5 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v3 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v2] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v1] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v5 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v5 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v1] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v2] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v4 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v1] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v5 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v3 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v5 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v3 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v3 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v2] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v3 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v4 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v5 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v4 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v2] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v1] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v4 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v1] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v3 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v4 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v5 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v3 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v2] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v3 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v4 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v3 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v5 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v3 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v4 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v1] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v1] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v5 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v4 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v4 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v1] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v1] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v2] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v3 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v4 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v1] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v4 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v4 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v5 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v4 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v4 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v2] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v4 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v5 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v3 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v5 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v3 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v2] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v1] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v1] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v3 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v4 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v1] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v2] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v1] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v1] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v4 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v2] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v5 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v2] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v4 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v3 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v1] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v4 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v5 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v5 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v3 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v3 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v5 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v1] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v1] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v3 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v2] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v2] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v1] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v3 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v2] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v1] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v4 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v2] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v2] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v1] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v1] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v4 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v3 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v3 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v4 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v3 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v3 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v3 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v1] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v4 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v4 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v3 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v5 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v3 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v1] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v1] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v3 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v2] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v1] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v1] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v5 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v2] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v5 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v5 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v3 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v3 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v1] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v3 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v1] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v4 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v4 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v3 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v1] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v2] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v5 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v5 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v2] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v2] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v2] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v4 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v3 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v3 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v3 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v2] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v5 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v4 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v2] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v4 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v3 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v2] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v4 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v5 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v2] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v1] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v1] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v4 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v5 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v3 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v1] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v1] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v5 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v4 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v3 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v2] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v4 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v2] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v3 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v3 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v4 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v2] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v5 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v2] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v2] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v3 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v1] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v3 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v3 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v5 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v2] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v5 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v3 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v3 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v4 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v2] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v4 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v4 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v2] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v4 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v4 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v5 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v5 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v3 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v4 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v3 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v2] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v3 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v3 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v2] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v4 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v5 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v5 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v4 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v2] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v1] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v3 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v4 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v5 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v1] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v3 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v2] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v3 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v3 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v1] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v5 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v2] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v5 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v5 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v2] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v3 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v3 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v5 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v2] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v2] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v1] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v3 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v3 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v2] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v1] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v3 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v1] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v1] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v1] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v4 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v1] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v5 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v1] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v2] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v1] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v4 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v4 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v5 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v1] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v3 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v2] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v4 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v5 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v4 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v3 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v4 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v3 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v3 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v4 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v4 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v1] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v1] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v4 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v3 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v2] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v4 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v2] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v4 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v4 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v1] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v4 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v1] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v3 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v4 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v1] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v5 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v1] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v4 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v2] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v2] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v4 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v4 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v2] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v2] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v1] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v5 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v4 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v3 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v2] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v4 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v4 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v1] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v2] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v1] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v1] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v4 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v3 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v1] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v5 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v4 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v4 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v4 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v5 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v5 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v1] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v1] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v2] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v2] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v4 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v4 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v3 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v1] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v2] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v2] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v5 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v2] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v5 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v2] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v1] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v3 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v5 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v5 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v5 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v1] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v5 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v3 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v4 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v5 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v4 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v4 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v1] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v1] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v3 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v1] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v3 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v3 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v4 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v5 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v2] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v3 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v1] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v4 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v2] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v2] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v2] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v2] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v1] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v5 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v2] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v5 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v3 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v5 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v2] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v4 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v2] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v3 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v1] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v1] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v5 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v5 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v5 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v2] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v1] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v3 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v5 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v2] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v4 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v1] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v4 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v4 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v3 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v4 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v5 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v2] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v2] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v1] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v2] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v3 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v2] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v2] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v3 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v2] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v3 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v1] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v1] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v3 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v2] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v5 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v1] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v1] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v1] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v3 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v1] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v4 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v4 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v3 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v3 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v3 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v2] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v5 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v5 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v1] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v1] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v2] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v1] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v4 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v2] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v2] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v4 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v5 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v3 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v4 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v4 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v1] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v1] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v3 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v3 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v3 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v5 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v1] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v1] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v5 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v4 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v1] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v4 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v4 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v3 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v4 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v5 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v5 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v5 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v1] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v5 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v5 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v3 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v5 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v5 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v2] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v2] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v5 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v2] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v3 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v5 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v5 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v3 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v5 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v1] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v2] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v4 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v5 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v2] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v3 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v1] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v4 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v3 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v5 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v4 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v3 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v5 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v5 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v5 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v4 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v1] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v4 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v1] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v5 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v4 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v4 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v4 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v1] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v2] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v2] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v5 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v5 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v5 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v4 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v5 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v4 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v5 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v1] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v5 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v2] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v5 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v3 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v3 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v4 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v2] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v4 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v3 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v3 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v3 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v3 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v2] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v3 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v4 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v2] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v2] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v2] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v1] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v3 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v3 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v3 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v1] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v3 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v4 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v1] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v1] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v3 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v1] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v4 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v5 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v2] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v5 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v3 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v2] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v2] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v4 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v5 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v3 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v3 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v1] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v2] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v4 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v2] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v3 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v5 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v5 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v3 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v2] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v3 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v3 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v1] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v1] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v4 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v5 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v4 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v3 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v3 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v3 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v5 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v3 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v5 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v2] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v5 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v2] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v1] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v3 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v2] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v5 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v5 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v4 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v1] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v1] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v1] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v1] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v2] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v1] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v5 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v1] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v3 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v1] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v1] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v2] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v1] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v2] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v1] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v3 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v2] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v5 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v2] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v5 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v2] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v5 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v5 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v3 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v5 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v3 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v1] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v2] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v1] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v5 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v3 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v5 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v2] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v4 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v1] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v2] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v5 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v3 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v5 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v5 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v5 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v1] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v3 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v3 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v4 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v2] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v1] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v3 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v3 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v2] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v5 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v5 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v3 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v1] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v2] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v5 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v3 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v2] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v1] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v1] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v4 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v5 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v2] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v3 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v2] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v1] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v1] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v1] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v4 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v3 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v4 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v1] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v3 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v4 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v2] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v5 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v5 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v5 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v4 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v4 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v5 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v5 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v3 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v1] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v5 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v3 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v4 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v1] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v4 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v2] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v4 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v3 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v2] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v2] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v3 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v4 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v4 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 08:14 [PATCH v1] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-04 14:20 Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-07 08:17 ` Re: Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-10 07:47 ` Re: Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2025-08-28 16:07 ` Re: Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2026-03-30 20:19 ` Re: Adding per backend commit and rollback counters Kuba Knysiak <[email protected]>
2026-03-31 09:24 ` Re: Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2026-07-06 11:41 ` Re: Adding per backend commit and rollback counters Nishant Sharma <[email protected]>
This inbox is served by agora; see mirroring instructions
for how to clone and mirror all data and code used for this inbox